mirror of
https://github.com/cozystack/cozystack.git
synced 2026-11-12 23:54:01 +09:00
fix(observability-proxy): round the step up to whole milliseconds
The step is sent to vlselect in milliseconds and was truncated, so a window that the bucket cap does not divide evenly got one bucket more than --logs-max-buckets allows. Round it up before the window is shrunk to fit the step, so both caps still hold. Bare-seconds steps and start offsets are now tested on the accepting side too: only the refused forms were, and disabling the digit branch passed the suite. Assisted-by: LLM Signed-off-by: ZverGuy <maximbel2003@gmail.com>
This commit is contained in:
1 parent
a56ff5050f
commit
28b391ff17
2 files changed
+28
No files matched your search
@@ -179,6 +179,10 @@ func setStep(out, in url.Values, start, end time.Time, lim LogsLimits) *HTTPErro
|
||||
}
|
||||
step = max(min(d, span, lim.MaxRange/3), minStep)
|
||||
}
|
||||
// vlselect gets whole milliseconds; rounding down would add a bucket.
|
||||
if r := step % time.Millisecond; r != 0 {
|
||||
step += time.Millisecond - r
|
||||
}
|
||||
if span+2*step > lim.MaxRange {
|
||||
start = end.Add(-max(lim.MaxRange-2*step, 0))
|
||||
}
|
||||
|
||||
@@ -227,6 +227,12 @@ func TestBuildLogsParamsStepAndLimit(t *testing.T) {
|
||||
if got := build(t, "stats_query_range", with(day, "step", "1w")).Get("step"); got != "86400000ms" {
|
||||
t.Errorf("1w step on a day window = %s, want capped at the window", got)
|
||||
}
|
||||
if got := build(t, "hits", with(day, "step", "3600")).Get("step"); got != "3600000ms" {
|
||||
t.Errorf("bare-seconds step = %s, want 3600000ms", got)
|
||||
}
|
||||
if got := build(t, "hits", with(day, "step", "5400.5")).Get("step"); got != "5400500ms" {
|
||||
t.Errorf("fractional bare-seconds step = %s, want 5400500ms", got)
|
||||
}
|
||||
if got := build(t, "hits", day).Get("step"); got != "86400ms" {
|
||||
t.Errorf("omitted step = %s, want the bucket-bounded minimum", got)
|
||||
}
|
||||
@@ -271,6 +277,7 @@ func TestBuildLogsParamsTail(t *testing.T) {
|
||||
{"since in the future", url.Values{"since": {"2026-11-04T13:00:00Z"}}, "0ms"},
|
||||
{"start_offset within cap", url.Values{"start_offset": {"2m"}}, "120000ms"},
|
||||
{"start_offset over cap", url.Values{"start_offset": {"30d"}}, "900000ms"},
|
||||
{"start_offset in bare seconds", url.Values{"start_offset": {"120"}}, "120000ms"},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
@@ -328,3 +335,20 @@ func TestBuildLogsParamsStepCannotWidenWindow(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuildLogsParamsStepKeepsBucketCap(t *testing.T) {
|
||||
lim := testLimits()
|
||||
// Spans that MaxBuckets does not divide into whole milliseconds.
|
||||
for _, start := range []string{"2026-11-03T12:00:00.0007Z", "2026-11-04T11:59:58.3333Z", "6d23h59m59s"} {
|
||||
out := build(t, "hits", url.Values{"start": {start}})
|
||||
step, err := parseDuration(out.Get("step"))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
s, _ := time.Parse(time.RFC3339Nano, out.Get("start"))
|
||||
e, _ := time.Parse(time.RFC3339Nano, out.Get("end"))
|
||||
if buckets := (e.Sub(s) + step - 1) / step; int(buckets) > lim.MaxBuckets {
|
||||
t.Errorf("start %s: step %s gives %d buckets, more than %d", start, out.Get("step"), buckets, lim.MaxBuckets)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user