Files
LocalAI/pkg/model/initializers_load_logging_test.go
mudler's LocalAI [bot] 54d5c18bfb fix(model): only announce a load at INFO when a load actually happens (#11017)
backendLoader logged "BackendLoader starting" at INFO as its very first
statement, unconditionally. That reads as "a model is being loaded", but
backendLoader is not only a load path: in distributed mode Load()
deliberately bypasses the local cache and calls backendLoader on every
inference request so SmartRouter can re-pick a replica per request. The
model is already resident, no process is spawned, and nothing is loaded,
yet the banner fires at request rate.

On a live cluster this produced ~5 "BackendLoader starting" lines per
second for a single embedding model, sustained, starting 22 seconds
after the load had already completed. The model was state=loaded with
in_flight=0 and exactly one backend process on the worker. It looked
exactly like a retry storm and cost real debugging time during an
unrelated production investigation. The adjacent "effective runtime
tuning" banner, documented as "logged once per load", had the same
problem for the same reason.

Emit both banners at INFO only when the model is not already resident,
and keep the per-call trace at DEBUG for anyone following the routing
path. isResident is a plain store lookup with no health probe and no
eviction, so it is safe on the per-request hot path (unlike
checkIsLoaded, which probes and can evict).

Same class of defect as #10985: a log line that sends the reader after
the wrong thing.


Assisted-by: Claude Code:claude-opus-4-8[1m] [Read] [Edit] [Bash]

Signed-off-by: Ettore Di Giacinto <mudler@localai.io>
Co-authored-by: Ettore Di Giacinto <mudler@localai.io>
2026-07-21 15:33:29 +02:00

103 lines
3.7 KiB
Go

package model
import (
"bytes"
"log/slog"
pb "github.com/mudler/LocalAI/pkg/grpc/proto"
"github.com/mudler/LocalAI/pkg/system"
"github.com/mudler/xlog"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)
// backendLoader is not a synonym for "a model is being loaded". In distributed
// mode Load() deliberately bypasses the local cache and calls backendLoader on
// every inference request so SmartRouter can re-pick a replica per request, so
// the function runs at request rate against an already-resident model. Emitting
// the load banner unconditionally made ordinary embedding traffic (~5 req/s)
// look like a retry storm in the INFO log and sent an engineer chasing a
// non-existent hot loop during an unrelated production investigation. INFO must
// mark a genuine cold load only; the per-call trace belongs at DEBUG.
var _ = Describe("backendLoader load logging", func() {
var (
ml *ModelLoader
captured *bytes.Buffer
)
BeforeEach(func() {
systemState, err := system.GetSystemState(system.WithModelPath(GinkgoT().TempDir()))
Expect(err).ToNot(HaveOccurred())
ml = NewModelLoader(systemState)
// Capture at info level so a debug-level emission is filtered out: the
// assertions then fail on severity, not merely on wording.
captured = &bytes.Buffer{}
handler := slog.NewTextHandler(captured, &slog.HandlerOptions{Level: slog.LevelInfo})
xlog.SetLogger(xlog.NewLoggerWithHandler(handler, xlog.LogLevelInfo))
})
AfterEach(func() {
// xlog exposes no getter for the package logger, so restore the same
// default the suite entrypoint installs rather than the prior value.
xlog.SetLogger(xlog.NewLogger(xlog.LogLevel("info"), "text"))
})
Context("when the model is already resident", func() {
BeforeEach(func() {
resident := NewModel("resident-model", "127.0.0.1:65535", nil)
// Skip the gRPC health probe so the resident model survives the
// lookup without a live backend behind the address.
resident.MarkHealthy()
ml.mu.Lock()
ml.store.Set("resident-model", resident)
ml.mu.Unlock()
})
It("does not announce a load at info level", func() {
_, err := ml.backendLoader(
WithModelID("resident-model"),
WithModel("resident-model"),
WithBackendString("llama-cpp"),
WithLoadGRPCLoadModelOpts(&pb.ModelOptions{ContextSize: 4096}),
)
Expect(err).ToNot(HaveOccurred())
Expect(captured.String()).ToNot(ContainSubstring("BackendLoader starting"),
"a request served by an already-resident model must not look like a cold load")
})
It("does not repeat the effective runtime tuning banner at info level", func() {
_, err := ml.backendLoader(
WithModelID("resident-model"),
WithModel("resident-model"),
WithBackendString("llama-cpp"),
WithLoadGRPCLoadModelOpts(&pb.ModelOptions{ContextSize: 4096}),
)
Expect(err).ToNot(HaveOccurred())
Expect(captured.String()).ToNot(ContainSubstring("effective runtime tuning"),
"the tuning banner documents what a load will run with, so it belongs to a load")
})
})
Context("when the model is not resident", func() {
It("still announces the cold load at info level", func() {
// The load itself fails (no such backend is installed); what is
// pinned here is that the banner is emitted before that, so
// suppressing the warm case does not silence real loads.
_, err := ml.backendLoader(
WithModelID("cold-model"),
WithModel("cold-model"),
WithBackendString("definitely-not-an-installed-backend"),
WithLoadGRPCLoadModelOpts(&pb.ModelOptions{ContextSize: 4096}),
)
Expect(err).To(HaveOccurred())
Expect(captured.String()).To(ContainSubstring("BackendLoader starting"))
Expect(captured.String()).To(ContainSubstring("effective runtime tuning"))
})
})
})