diff --git a/pkg/query-service/app/clickhouseReader/reader.go b/pkg/query-service/app/clickhouseReader/reader.go index de49aa0e49..23c09ee720 100644 --- a/pkg/query-service/app/clickhouseReader/reader.go +++ b/pkg/query-service/app/clickhouseReader/reader.go @@ -2591,6 +2591,32 @@ func (r *ClickHouseReader) SetTTL(ctx context.Context, return &model.SetTTLResponseItem{Message: "move ttl has been successfully set up"}, nil } +func (r *ClickHouseReader) RemoveTTL(ctx context.Context, + params *model.RemoveTTLParams) (*model.RemoveTTLResponseItem, *model.ApiError) { + + var reqs []string + tracesQuery := fmt.Sprintf("ALTER TABLE default.%v REMOVE TTL", signozTraceTableName) + metricsQuery := fmt.Sprintf("ALTER TABLE %v REMOVE TTL", signozMetricDBName+"."+signozSampleName) + switch params.Type { + case constants.TraceTTL: + reqs = append(reqs, tracesQuery) + case constants.MetricsTTL: + reqs = append(reqs, metricsQuery) + default: + reqs = append(reqs, tracesQuery, metricsQuery) + } + + zap.S().Debugf("Executing remove TTL requests: %s\n", reqs) + for _, req := range reqs { + if _, err := r.db.Exec(req); err != nil { + zap.S().Error(fmt.Errorf("error while removing ttl. Err=%v", err)) + return nil, &model.ApiError{model.ErrorExec, + fmt.Errorf("error while removing ttl. Err=%v", err)} + } + } + return &model.RemoveTTLResponseItem{Message: "ttl has been successfully removed"}, nil +} + // GetDisks returns a list of disks {name, type} configured in clickhouse DB. func (r *ClickHouseReader) GetDisks(ctx context.Context) (*[]model.DiskItem, *model.ApiError) { diskItems := []model.DiskItem{} diff --git a/pkg/query-service/app/druidReader/reader.go b/pkg/query-service/app/druidReader/reader.go index 1c077466a6..7ad9fd59ea 100644 --- a/pkg/query-service/app/druidReader/reader.go +++ b/pkg/query-service/app/druidReader/reader.go @@ -10,8 +10,8 @@ import ( "github.com/prometheus/prometheus/util/stats" "go.signoz.io/query-service/druidQuery" "go.signoz.io/query-service/godruid" - "go.signoz.io/query-service/model" am "go.signoz.io/query-service/integrations/alertManager" + "go.signoz.io/query-service/model" ) type DruidReader struct { @@ -201,3 +201,7 @@ func (druid *DruidReader) GetErrorForId(_ context.Context, _ *model.GetErrorPara func (druid *DruidReader) GetErrorForType(_ context.Context, _ *model.GetErrorParams) (*model.ErrorWithSpan, *model.ApiError) { return nil, &model.ApiError{model.ErrorNotImplemented, fmt.Errorf("druid does not support get error API")} } + +func (druid *DruidReader) RemoveTTL(_ context.Context, _ *model.RemoveTTLParams) (*model.RemoveTTLResponseItem, *model.ApiError) { + return nil, &model.ApiError{model.ErrorNotImplemented, fmt.Errorf("druid does not support ttl configuration")} +} diff --git a/pkg/query-service/app/http_handler.go b/pkg/query-service/app/http_handler.go index 771637935a..d5427c5c93 100644 --- a/pkg/query-service/app/http_handler.go +++ b/pkg/query-service/app/http_handler.go @@ -12,11 +12,12 @@ import ( _ "github.com/mattn/go-sqlite3" "github.com/prometheus/prometheus/promql" "go.signoz.io/query-service/app/dashboards" + "go.signoz.io/query-service/constants" "go.signoz.io/query-service/dao/interfaces" + am "go.signoz.io/query-service/integrations/alertManager" "go.signoz.io/query-service/model" "go.signoz.io/query-service/telemetry" "go.signoz.io/query-service/version" - am "go.signoz.io/query-service/integrations/alertManager" "go.uber.org/zap" ) @@ -205,6 +206,7 @@ func (aH *APIHandler) RegisterRoutes(router *mux.Router) { router.HandleFunc("/api/v1/serviceMapDependencies", aH.serviceMapDependencies).Methods(http.MethodGet) router.HandleFunc("/api/v1/settings/ttl", aH.setTTL).Methods(http.MethodPost) router.HandleFunc("/api/v1/settings/ttl", aH.getTTL).Methods(http.MethodGet) + router.HandleFunc("/api/v1/settings/ttl", aH.removeTTL).Methods(http.MethodDelete) router.HandleFunc("/api/v1/userPreferences", aH.setUserPreferences).Methods(http.MethodPost) router.HandleFunc("/api/v1/userPreferences", aH.getUserPreferences).Methods(http.MethodGet) @@ -1090,6 +1092,47 @@ func (aH *APIHandler) getTTL(w http.ResponseWriter, r *http.Request) { aH.writeJSON(w, r, result) } +func (aH *APIHandler) removeTTL(w http.ResponseWriter, r *http.Request) { + ttlParams, err := parseRemoveTTL(r) + if aH.handleError(w, err, http.StatusBadRequest) { + return + } + + existingTTL, apiErr := (*aH.reader).GetTTL(context.Background(), &model.GetTTLParams{GetAllTTL: true}) + if apiErr != nil && aH.handleError(w, apiErr.Err, http.StatusInternalServerError) { + return + } + + if ttlParams.Type == constants.TraceTTL && existingTTL.TracesTime == -1 && + aH.handleError(w, fmt.Errorf("traces doesn't have any TTL set, cannot remove"), http.StatusBadRequest) { + return + } + + if ttlParams.Type == constants.MetricsTTL && existingTTL.MetricsTime == -1 && + aH.handleError(w, fmt.Errorf("metrics doesn't have any TTL set, cannot remove"), http.StatusBadRequest) { + return + } + + if ttlParams.RemoveAllTTL { + if existingTTL.TracesTime == -1 && existingTTL.MetricsTime != -1 { + ttlParams.Type = constants.MetricsTTL + ttlParams.RemoveAllTTL = false + } else if existingTTL.TracesTime != -1 && existingTTL.MetricsTime == -1 { + ttlParams.Type = constants.TraceTTL + ttlParams.RemoveAllTTL = false + } else if aH.handleError(w, fmt.Errorf("no TTL set, cannot remove"), http.StatusBadRequest) { + return + } + } + + result, apiErr := (*aH.reader).RemoveTTL(context.Background(), ttlParams) + if apiErr != nil && aH.handleError(w, apiErr.Err, http.StatusInternalServerError) { + return + } + + aH.writeJSON(w, r, result) +} + func (aH *APIHandler) getDisks(w http.ResponseWriter, r *http.Request) { result, apiErr := (*aH.reader).GetDisks(context.Background()) if apiErr != nil && aH.handleError(w, apiErr.Err, http.StatusInternalServerError) { diff --git a/pkg/query-service/app/interface.go b/pkg/query-service/app/interface.go index 8a979090d1..bbc15eae89 100644 --- a/pkg/query-service/app/interface.go +++ b/pkg/query-service/app/interface.go @@ -5,8 +5,8 @@ import ( "github.com/prometheus/prometheus/promql" "github.com/prometheus/prometheus/util/stats" - "go.signoz.io/query-service/model" am "go.signoz.io/query-service/integrations/alertManager" + "go.signoz.io/query-service/model" ) type Reader interface { @@ -58,4 +58,5 @@ type Reader interface { // Setter Interfaces SetTTL(ctx context.Context, ttlParams *model.TTLParams) (*model.SetTTLResponseItem, *model.ApiError) + RemoveTTL(ctx context.Context, ttlParams *model.RemoveTTLParams) (*model.RemoveTTLResponseItem, *model.ApiError) } diff --git a/pkg/query-service/app/parser.go b/pkg/query-service/app/parser.go index ae9abc5204..1958260ad7 100644 --- a/pkg/query-service/app/parser.go +++ b/pkg/query-service/app/parser.go @@ -902,7 +902,7 @@ func parseTTLParams(r *http.Request) (*model.TTLParams, error) { // Validate the TTL duration. durationParsed, err := time.ParseDuration(delDuration) - if err != nil { + if err != nil || durationParsed.Seconds() <= 0 { return nil, fmt.Errorf("Not a valid TTL duration %v", delDuration) } @@ -911,7 +911,7 @@ func parseTTLParams(r *http.Request) (*model.TTLParams, error) { // If some cold storage is provided, validate the cold storage move TTL. if len(coldStorage) > 0 { toColdParsed, err = time.ParseDuration(toColdDuration) - if err != nil { + if err != nil || toColdParsed.Seconds() <= 0 { return nil, fmt.Errorf("Not a valid toCold TTL duration %v", toColdDuration) } if toColdParsed.Seconds() != 0 && toColdParsed.Seconds() >= durationParsed.Seconds() { @@ -944,6 +944,23 @@ func parseGetTTL(r *http.Request) (*model.GetTTLParams, error) { return &model.GetTTLParams{Type: typeTTL, GetAllTTL: getAllTTL}, nil } +func parseRemoveTTL(r *http.Request) (*model.RemoveTTLParams, error) { + + typeTTL := r.URL.Query().Get("type") + removeAllTTL := false + + if len(typeTTL) == 0 { + removeAllTTL = true + } else { + // Validate the type parameter + if typeTTL != constants.TraceTTL && typeTTL != constants.MetricsTTL { + return nil, fmt.Errorf("type param should be , got %v", typeTTL) + } + } + + return &model.RemoveTTLParams{Type: typeTTL, RemoveAllTTL: removeAllTTL}, nil +} + func parseUserPreferences(r *http.Request) (*model.UserPreferences, error) { var userPreferences model.UserPreferences diff --git a/pkg/query-service/model/queryParams.go b/pkg/query-service/model/queryParams.go index 938ce02151..ac9205ef13 100644 --- a/pkg/query-service/model/queryParams.go +++ b/pkg/query-service/model/queryParams.go @@ -229,3 +229,8 @@ type GetErrorParams struct { ErrorID string ServiceName string } + +type RemoveTTLParams struct { + Type string + RemoveAllTTL bool +} diff --git a/pkg/query-service/model/response.go b/pkg/query-service/model/response.go index 8d4bd4b766..c7e9ded5c2 100644 --- a/pkg/query-service/model/response.go +++ b/pkg/query-service/model/response.go @@ -273,6 +273,10 @@ type SetTTLResponseItem struct { Message string `json:"message"` } +type RemoveTTLResponseItem struct { + Message string `json:"message"` +} + type DiskItem struct { Name string `json:"name,omitempty" db:"name,omitempty"` Type string `json:"type,omitempty" db:"type,omitempty"`