Skip to content

Commit 399bf97

Browse files
committed
do not flood of healthy checks
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>
1 parent 64c1576 commit 399bf97

2 files changed

Lines changed: 35 additions & 4 deletions

File tree

pkg/model/loader.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -326,6 +326,15 @@ func (ml *ModelLoader) checkIsLoaded(s string) *Model {
326326
}
327327

328328
xlog.Debug("Model already loaded in memory", "model", s)
329+
330+
// Skip the gRPC health check if the model was recently verified.
331+
// This avoids serializing concurrent requests behind ml.mu while each
332+
// one does a network round-trip (especially costly in distributed mode).
333+
if m.IsRecentlyHealthy() {
334+
xlog.Debug("Model health check cached, skipping gRPC probe", "model", s)
335+
return m
336+
}
337+
329338
client := m.GRPC(false, ml.wd)
330339

331340
xlog.Debug("Checking model availability", "model", s)
@@ -352,5 +361,6 @@ func (ml *ModelLoader) checkIsLoaded(s string) *Model {
352361
}
353362
}
354363

364+
m.MarkHealthy()
355365
return m
356366
}

pkg/model/model.go

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,23 @@ package model
22

33
import (
44
"sync"
5+
"time"
56

67
grpc "github.com/mudler/LocalAI/pkg/grpc"
78
process "github.com/mudler/go-processmanager"
89
)
910

11+
// healthCheckTTL is the duration for which a successful health check is cached.
12+
// Subsequent checkIsLoaded calls within this window skip the gRPC round-trip,
13+
// avoiding serialization of concurrent requests behind ml.mu.Lock().
14+
const healthCheckTTL = 30 * time.Second
15+
1016
type Model struct {
11-
ID string `json:"id"`
12-
address string
13-
client grpc.Backend
14-
process *process.Process
17+
ID string `json:"id"`
18+
address string
19+
client grpc.Backend
20+
process *process.Process
21+
lastHealthCheck time.Time
1522
sync.Mutex
1623
}
1724

@@ -37,6 +44,20 @@ func (m *Model) Process() *process.Process {
3744
return m.process
3845
}
3946

47+
// IsRecentlyHealthy returns true if the model passed a health check within the TTL.
48+
func (m *Model) IsRecentlyHealthy() bool {
49+
m.Lock()
50+
defer m.Unlock()
51+
return !m.lastHealthCheck.IsZero() && time.Since(m.lastHealthCheck) < healthCheckTTL
52+
}
53+
54+
// MarkHealthy records the current time as the last successful health check.
55+
func (m *Model) MarkHealthy() {
56+
m.Lock()
57+
defer m.Unlock()
58+
m.lastHealthCheck = time.Now()
59+
}
60+
4061
func (m *Model) GRPC(parallel bool, wd *WatchDog) grpc.Backend {
4162
if m.client != nil {
4263
return m.client

0 commit comments

Comments
 (0)