2025-05-25 22:14:47 +05:30
|
|
|
package querybuilder
|
|
|
|
|
|
2025-06-23 14:00:50 +05:30
|
|
|
import (
|
|
|
|
|
"fmt"
|
|
|
|
|
"math"
|
2025-07-18 18:37:57 +05:30
|
|
|
"time"
|
|
|
|
|
|
|
|
|
|
"github.com/SigNoz/signoz/pkg/types/metrictypes"
|
|
|
|
|
qbtypes "github.com/SigNoz/signoz/pkg/types/querybuildertypes/querybuildertypesv5"
|
2025-06-23 14:00:50 +05:30
|
|
|
)
|
2025-05-25 22:14:47 +05:30
|
|
|
|
2025-05-27 20:54:48 +05:30
|
|
|
const (
|
|
|
|
|
NsToSeconds = 1000000000
|
|
|
|
|
BucketAdjustment = 1800 // 30 minutes
|
2025-06-21 17:49:33 +05:30
|
|
|
|
|
|
|
|
RecommendedNumberOfPoints = 300
|
|
|
|
|
MaxAllowedNumberOfPoints = 1500
|
|
|
|
|
MaxAllowedSeries = 3000
|
2025-05-27 20:54:48 +05:30
|
|
|
)
|
|
|
|
|
|
2025-05-25 22:14:47 +05:30
|
|
|
// ToNanoSecs takes epoch and returns it in ns
|
|
|
|
|
func ToNanoSecs(epoch uint64) uint64 {
|
|
|
|
|
temp := epoch
|
|
|
|
|
count := 0
|
|
|
|
|
if epoch == 0 {
|
|
|
|
|
count = 1
|
|
|
|
|
} else {
|
|
|
|
|
for epoch != 0 {
|
|
|
|
|
epoch /= 10
|
|
|
|
|
count++
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return temp * uint64(math.Pow(10, float64(19-count)))
|
|
|
|
|
}
|
2025-06-21 17:49:33 +05:30
|
|
|
|
2025-08-06 23:05:39 +05:30
|
|
|
// TODO(srikanthccv): should these be rounded to nearest multiple of 60 instead of 5 if step > 60?
|
|
|
|
|
// That would make graph look nice but "nice" but should be less important than the usefulness
|
2025-06-21 17:49:33 +05:30
|
|
|
func RecommendedStepInterval(start, end uint64) uint64 {
|
|
|
|
|
start = ToNanoSecs(start)
|
|
|
|
|
end = ToNanoSecs(end)
|
|
|
|
|
|
|
|
|
|
step := (end - start) / RecommendedNumberOfPoints / 1e9
|
|
|
|
|
|
|
|
|
|
if step < 5 {
|
|
|
|
|
return 5
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// return the nearest lower multiple of 5
|
|
|
|
|
return step - step%5
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func MinAllowedStepInterval(start, end uint64) uint64 {
|
|
|
|
|
start = ToNanoSecs(start)
|
|
|
|
|
end = ToNanoSecs(end)
|
|
|
|
|
|
|
|
|
|
step := (end - start) / MaxAllowedNumberOfPoints / 1e9
|
|
|
|
|
|
|
|
|
|
if step < 5 {
|
|
|
|
|
return 5
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// return the nearest lower multiple of 5
|
|
|
|
|
return step - step%5
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func RecommendedStepIntervalForMetric(start, end uint64) uint64 {
|
|
|
|
|
start = ToNanoSecs(start)
|
|
|
|
|
end = ToNanoSecs(end)
|
|
|
|
|
|
|
|
|
|
step := (end - start) / RecommendedNumberOfPoints / 1e9
|
|
|
|
|
|
|
|
|
|
// TODO(srikanthccv): make this make use of the reporting frequency and remove the 60
|
|
|
|
|
if step < 60 {
|
|
|
|
|
return 60
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// return the nearest lower multiple of 60
|
2025-07-18 18:37:57 +05:30
|
|
|
recommended := step - step%60
|
|
|
|
|
|
|
|
|
|
// if the time range is greater than 1 day, and less than 1 week set the step interval to be multiple of 5 minutes
|
|
|
|
|
// if the time range is greater than 1 week, set the step interval to be multiple of 30 mins
|
|
|
|
|
if end-start >= uint64(24*time.Hour.Nanoseconds()) && end-start < uint64(7*24*time.Hour.Nanoseconds()) {
|
|
|
|
|
recommended = uint64(math.Round(float64(recommended)/300)) * 300
|
|
|
|
|
} else if end-start >= uint64(7*24*time.Hour.Nanoseconds()) {
|
|
|
|
|
recommended = uint64(math.Round(float64(recommended)/1800)) * 1800
|
|
|
|
|
}
|
|
|
|
|
return recommended
|
2025-06-21 17:49:33 +05:30
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func MinAllowedStepIntervalForMetric(start, end uint64) uint64 {
|
|
|
|
|
start = ToNanoSecs(start)
|
|
|
|
|
end = ToNanoSecs(end)
|
|
|
|
|
|
|
|
|
|
step := (end - start) / RecommendedNumberOfPoints / 1e9
|
|
|
|
|
|
|
|
|
|
// TODO(srikanthccv): make this make use of the reporting frequency and remove the 60
|
|
|
|
|
if step < 60 {
|
|
|
|
|
return 60
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// return the nearest lower multiple of 60
|
2025-07-18 18:37:57 +05:30
|
|
|
minAllowed := step - step%60
|
|
|
|
|
|
|
|
|
|
// if the time range is greater than 1 day, and less than 1 week set the step interval to be multiple of 5 minutes
|
|
|
|
|
// if the time range is greater than 1 week, set the step interval to be multiple of 30 mins
|
|
|
|
|
if end-start >= uint64(24*time.Hour.Nanoseconds()) && end-start < uint64(7*24*time.Hour.Nanoseconds()) {
|
|
|
|
|
minAllowed = uint64(math.Round(float64(minAllowed)/300)) * 300
|
|
|
|
|
} else if end-start >= uint64(7*24*time.Hour.Nanoseconds()) {
|
|
|
|
|
minAllowed = uint64(math.Round(float64(minAllowed)/1800)) * 1800
|
|
|
|
|
}
|
|
|
|
|
return minAllowed
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func AdjustedMetricTimeRange(start, end, step uint64, mq qbtypes.QueryBuilderQuery[qbtypes.MetricAggregation]) (uint64, uint64) {
|
|
|
|
|
// align the start to the step interval
|
|
|
|
|
start = start - (start % (step * 1000))
|
|
|
|
|
// if the query is a rate query, we adjust the start time by one more step
|
|
|
|
|
// so that we can calculate the rate for the first data point
|
|
|
|
|
hasRunningDiff := false
|
|
|
|
|
for _, fn := range mq.Functions {
|
|
|
|
|
if fn.Name == qbtypes.FunctionNameRunningDiff {
|
|
|
|
|
hasRunningDiff = true
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (mq.Aggregations[0].TimeAggregation == metrictypes.TimeAggregationRate || mq.Aggregations[0].TimeAggregation == metrictypes.TimeAggregationIncrease) &&
|
|
|
|
|
mq.Aggregations[0].Temporality != metrictypes.Delta {
|
|
|
|
|
start -= step * 1000
|
|
|
|
|
}
|
|
|
|
|
if hasRunningDiff {
|
|
|
|
|
start -= step * 1000
|
|
|
|
|
}
|
|
|
|
|
// align the end to the nearest minute
|
|
|
|
|
adjustStep := uint64(math.Min(float64(step), 60))
|
|
|
|
|
end = end - (end % (adjustStep * 1000))
|
|
|
|
|
return start, end
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-23 14:00:50 +05:30
|
|
|
func AssignReservedVars(vars map[string]any, start, end uint64) {
|
|
|
|
|
start = ToNanoSecs(start)
|
|
|
|
|
end = ToNanoSecs(end)
|
|
|
|
|
|
|
|
|
|
vars["start_timestamp"] = start / 1_000_000_000
|
|
|
|
|
vars["end_timestamp"] = end / 1_000_000_000
|
|
|
|
|
vars["start_timestamp_ms"] = start / 1_000_000
|
|
|
|
|
vars["end_timestamp_ms"] = end / 1_000_000
|
|
|
|
|
vars["SIGNOZ_START_TIME"] = start / 1_000_000
|
|
|
|
|
vars["SIGNOZ_END_TIME"] = end / 1_000_000
|
|
|
|
|
vars["start_timestamp_nano"] = start
|
|
|
|
|
vars["end_timestamp_nano"] = end
|
|
|
|
|
vars["start_datetime"] = fmt.Sprintf("toDateTime(%d)", start/1_000_000_000)
|
|
|
|
|
vars["end_datetime"] = fmt.Sprintf("toDateTime(%d)", end/1_000_000_000)
|
|
|
|
|
}
|