Skip to content

Commit 05ff401

Browse files
localai-botmudler
andauthored
fix(test): stop the backend-trace specs racing the lossy trace channel (#11146)
RecordBackendTrace does a non-blocking send onto a 100-slot channel and drops when it is full, so tracing never stalls inference. The payload bounding specs pushed all 200 traces in one tight loop, which overruns that channel on a loaded machine: entries are dropped for good and the Eventually waiting for 200 can never be satisfied, no matter the timeout. CI hit this on master at 0a8a7fb, settling at 158/200. Feed the traces in chunks of 50, draining after each, so the channel is never overrun and the count stays exact. Reproduced with 60 busy loops on a 20-core box at GOMAXPROCS=2: 0/12 runs passed before, 12/12 after. Assisted-by: Claude Code:claude-opus-5 [Read] [Edit] [Bash] Signed-off-by: Ettore Di Giacinto <mudler@localai.io> Co-authored-by: Ettore Di Giacinto <mudler@localai.io>
1 parent a7fa678 commit 05ff401

1 file changed

Lines changed: 22 additions & 5 deletions

File tree

core/http/endpoints/localai/traces_test.go

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,12 @@ var _ = Describe("Backend traces payload bounding", func() {
9696
// 3.4 MB: every entry carries the full input text.
9797
const heavyText = 8192
9898

99+
// totalTraces overfills the 100-slot trace channel on purpose (the specs
100+
// assert on paging and on X-Total-Count), so it has to be fed in chunks -
101+
// see the BeforeEach. traceChunk stays below that channel capacity.
102+
const totalTraces = 200
103+
const traceChunk = 50
104+
99105
BeforeEach(func() {
100106
app = echo.New()
101107
app.GET("/api/backend-traces", GetBackendTracesEndpoint())
@@ -104,7 +110,7 @@ var _ = Describe("Backend traces payload bounding", func() {
104110
trace.ClearBackendTraces()
105111
trace.InitBackendTracingIfEnabled(500, 0)
106112
base := time.Now()
107-
for i := range 200 {
113+
for i := range totalTraces {
108114
trace.RecordBackendTrace(trace.BackendTrace{
109115
// Distinct timestamps keep the newest-first ordering (and
110116
// therefore the offset paging) deterministic.
@@ -115,9 +121,20 @@ var _ = Describe("Backend traces payload bounding", func() {
115121
Body: strings.Repeat("b", heavyText),
116122
Data: map[string]any{"input_text": strings.Repeat("x", heavyText)},
117123
})
124+
// RecordBackendTrace is deliberately lossy: it does a non-blocking
125+
// send and drops when the channel is full, so tracing never stalls
126+
// inference. Pushing all 200 in one tight loop therefore overruns
127+
// the 100-slot channel on a loaded machine and the buffer settles
128+
// below 200 permanently - no amount of waiting recovers a dropped
129+
// trace. Drain in chunks well under the channel capacity instead,
130+
// which keeps the count exact regardless of scheduling.
131+
if (i+1)%traceChunk == 0 {
132+
Eventually(func() int { return len(trace.GetBackendTraces()) }, 30*time.Second, 10*time.Millisecond).
133+
Should(Equal(i + 1))
134+
}
118135
}
119-
// Recording is asynchronous through a channel; wait for the buffer.
120-
Eventually(func() int { return len(trace.GetBackendTraces()) }).Should(Equal(200))
136+
Eventually(func() int { return len(trace.GetBackendTraces()) }, 30*time.Second, 10*time.Millisecond).
137+
Should(Equal(totalTraces))
121138
})
122139

123140
AfterEach(func() {
@@ -138,7 +155,7 @@ var _ = Describe("Backend traces payload bounding", func() {
138155
var out []map[string]any
139156
Expect(json.Unmarshal(rec.Body.Bytes(), &out)).To(Succeed())
140157
Expect(out).To(HaveLen(DefaultTraceListLimit))
141-
Expect(rec.Header().Get("X-Total-Count")).To(Equal("200"))
158+
Expect(rec.Header().Get("X-Total-Count")).To(Equal(strconv.Itoa(totalTraces)))
142159
})
143160

144161
It("omits the heavy body and data fields from list entries", func() {
@@ -156,7 +173,7 @@ var _ = Describe("Backend traces payload bounding", func() {
156173
bounded := get("/api/backend-traces").Body.Len()
157174
unbounded := get("/api/backend-traces?limit=0&full=true").Body.Len()
158175

159-
Expect(unbounded).To(BeNumerically(">", 200*heavyText))
176+
Expect(unbounded).To(BeNumerically(">", totalTraces*heavyText))
160177
Expect(bounded).To(BeNumerically("<", unbounded/100))
161178
})
162179

0 commit comments

Comments
 (0)