mirror of
https://github.com/mudler/LocalAI.git
synced 2026-06-04 23:06:22 -04:00
feat(p2p): gossip free VRAM per node and add testable online check
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>
This commit is contained in:
@@ -15,6 +15,7 @@ import (
|
||||
"github.com/libp2p/go-libp2p/core/peer"
|
||||
"github.com/mudler/LocalAI/core/schema"
|
||||
"github.com/mudler/LocalAI/pkg/utils"
|
||||
"github.com/mudler/LocalAI/pkg/xsysinfo"
|
||||
"github.com/mudler/edgevpn/pkg/config"
|
||||
"github.com/mudler/edgevpn/pkg/node"
|
||||
"github.com/mudler/edgevpn/pkg/protocol"
|
||||
@@ -348,9 +349,10 @@ func ExposeService(ctx context.Context, host, port, token, servicesID string) (*
|
||||
func() {
|
||||
updatedMap := map[string]any{}
|
||||
updatedMap[name] = &schema.NodeData{
|
||||
Name: name,
|
||||
LastSeen: time.Now(),
|
||||
ID: nodeID(name),
|
||||
Name: name,
|
||||
LastSeen: time.Now(),
|
||||
ID: nodeID(name),
|
||||
AvailableVRAM: xsysinfo.GetGPUAggregateInfo().FreeVRAM,
|
||||
}
|
||||
ledger.Add(servicesID, updatedMap)
|
||||
},
|
||||
|
||||
@@ -121,18 +121,32 @@ type StoresFindResponse struct {
|
||||
Similarities []float32 `json:"similarities" yaml:"similarities"`
|
||||
}
|
||||
|
||||
// NodeOnlineWindow is how long after its last announce a node still counts as
|
||||
// online. Nodes re-announce into the edgevpn ledger every 20s (see core/p2p
|
||||
// ExposeService), so 40s tolerates a single missed announce.
|
||||
const NodeOnlineWindow = 40 * time.Second
|
||||
|
||||
type NodeData struct {
|
||||
Name string
|
||||
ID string
|
||||
TunnelAddress string
|
||||
ServiceID string
|
||||
LastSeen time.Time
|
||||
// AvailableVRAM is the node's free GPU VRAM in bytes at its last announce,
|
||||
// gossiped so federation selection can prefer peers with more headroom.
|
||||
// Zero for CPU-only nodes and for peers on an older version that does not
|
||||
// publish it; the routing policy treats zero as the lowest VRAM tier.
|
||||
AvailableVRAM uint64
|
||||
}
|
||||
|
||||
func (d NodeData) IsOnline() bool {
|
||||
now := time.Now()
|
||||
// if the node was seen in the last 40 seconds, it's online
|
||||
return now.Sub(d.LastSeen) < 40*time.Second
|
||||
return d.IsOnlineAt(time.Now())
|
||||
}
|
||||
|
||||
// IsOnlineAt reports whether the node counts as online relative to now. It is
|
||||
// split from IsOnline so selection logic can be exercised with a fixed clock.
|
||||
func (d NodeData) IsOnlineAt(now time.Time) bool {
|
||||
return now.Sub(d.LastSeen) < NodeOnlineWindow
|
||||
}
|
||||
|
||||
type P2PNodesResponse struct {
|
||||
|
||||
34
core/schema/nodedata_test.go
Normal file
34
core/schema/nodedata_test.go
Normal file
@@ -0,0 +1,34 @@
|
||||
package schema_test
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
. "github.com/onsi/gomega"
|
||||
|
||||
"github.com/mudler/LocalAI/core/schema"
|
||||
)
|
||||
|
||||
var _ = Describe("NodeData", func() {
|
||||
ref := time.Date(2026, 1, 1, 0, 0, 0, 0, time.UTC)
|
||||
|
||||
It("is online within the online window", func() {
|
||||
nd := schema.NodeData{LastSeen: ref.Add(-30 * time.Second)}
|
||||
Expect(nd.IsOnlineAt(ref)).To(BeTrue())
|
||||
})
|
||||
|
||||
It("is offline once the online window has elapsed", func() {
|
||||
nd := schema.NodeData{LastSeen: ref.Add(-50 * time.Second)}
|
||||
Expect(nd.IsOnlineAt(ref)).To(BeFalse())
|
||||
})
|
||||
|
||||
It("treats exactly the window boundary as offline (strict less-than)", func() {
|
||||
nd := schema.NodeData{LastSeen: ref.Add(-schema.NodeOnlineWindow)}
|
||||
Expect(nd.IsOnlineAt(ref)).To(BeFalse())
|
||||
})
|
||||
|
||||
It("carries AvailableVRAM in bytes", func() {
|
||||
nd := schema.NodeData{AvailableVRAM: 8_000_000_000}
|
||||
Expect(nd.AvailableVRAM).To(Equal(uint64(8_000_000_000)))
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user