Skip to content

Commit 94e79b7

Browse files
committed
feat: implement heartbeat management with long polling and WebSocket support
1 parent 4505719 commit 94e79b7

4 files changed

Lines changed: 294 additions & 89 deletions

File tree

.vscode/launch.json

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,17 @@
66
"configurations": [
77

88
{
9-
"name": "Debug Local Env Operator",
9+
"name": "Debug GPU Go",
1010
"type": "go",
1111
"request": "launch",
1212
"mode": "auto",
1313
"console": "integratedTerminal",
1414
"env": {
15+
"GPU_GO_HEARTBEAT_MODE": "long-polling",
16+
"GPU_GO_ENDPOINT": "http://127.0.0.1:8787"
1517
},
1618
"args": [
17-
"agent", "start", "--server", "http://192.168.2.37:8787"
19+
"agent", "start"
1820
],
1921
"program": "${workspaceFolder}/cmd/ggo/main.go",
2022
}

cmd/ggo/agent/agent.go

Lines changed: 51 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"os"
66
"os/signal"
77
"strconv"
8+
"sync"
89
"syscall"
910

1011
"github.com/NexusGPU/gpu-go/internal/agent"
@@ -26,6 +27,11 @@ var (
2627
acceleratorLib string
2728
isolationMode string
2829
paths = platform.DefaultPaths()
30+
31+
// Hypervisor singleton
32+
hypervisorOnce sync.Once
33+
hypervisorManager *hypervisor.Manager
34+
hypervisorErr error
2935
)
3036

3137
// NewAgentCmd creates the agent command
@@ -65,6 +71,45 @@ func getIsolationMode() tfv1.IsolationModeType {
6571
}
6672
}
6773

74+
// getHypervisorManager returns the singleton hypervisor manager, initializing it if needed
75+
func getHypervisorManager() (*hypervisor.Manager, error) {
76+
hypervisorOnce.Do(func() {
77+
if os.Getenv("GPU_GO_MOCK_GPUS") != "" {
78+
hypervisorErr = fmt.Errorf("mock mode enabled, hypervisor not available")
79+
return
80+
}
81+
82+
libPath := acceleratorLib
83+
if libPath == "" {
84+
libPath = agent.FindAcceleratorLibrary()
85+
}
86+
if libPath == "" {
87+
hypervisorErr = fmt.Errorf("accelerator library not found")
88+
return
89+
}
90+
91+
hypervisorManager, hypervisorErr = hypervisor.NewManager(hypervisor.Config{
92+
LibPath: libPath,
93+
Vendor: agent.DetectVendorFromLibPath(libPath),
94+
IsolationMode: getIsolationMode(),
95+
Logger: log.Logger,
96+
StateDir: stateDir,
97+
})
98+
if hypervisorErr != nil {
99+
return
100+
}
101+
hypervisorErr = hypervisorManager.Start()
102+
})
103+
return hypervisorManager, hypervisorErr
104+
}
105+
106+
// stopHypervisorManager stops the singleton hypervisor manager if running
107+
func stopHypervisorManager() {
108+
if hypervisorManager != nil {
109+
hypervisorManager.Stop()
110+
}
111+
}
112+
68113
func newRegisterCmd() *cobra.Command {
69114
var token string
70115

@@ -151,8 +196,8 @@ func newStartCmd() *cobra.Command {
151196
api.WithAgentSecret(cfg.AgentSecret),
152197
)
153198

154-
// Create hypervisor manager
155-
hvMgr, err := createHypervisorManager()
199+
// Get singleton hypervisor manager
200+
hvMgr, err := getHypervisorManager()
156201
if err != nil {
157202
// Log warning but continue - agent can work without hypervisor for some operations
158203
log.Warn().Err(err).Msg("Failed to initialize hypervisor manager, worker management will be limited")
@@ -196,6 +241,7 @@ func newStartCmd() *cobra.Command {
196241
}
197242

198243
agentInstance.Stop()
244+
stopHypervisorManager()
199245
return nil
200246
},
201247
}
@@ -329,38 +375,13 @@ func discoverGPUs() []api.GPUInfo {
329375
return agent.CreateMockGPUs(count)
330376
}
331377

332-
// Try to discover GPUs using hypervisor
333-
libPath := acceleratorLib
334-
if libPath == "" {
335-
libPath = agent.FindAcceleratorLibrary()
336-
}
337-
338-
if libPath == "" {
339-
log.Warn().Msg("No accelerator library found, set TENSOR_FUSION_LIB_PATH or use --accelerator-lib")
340-
return nil
341-
}
342-
343-
// Create temporary hypervisor manager for GPU discovery
344-
hvMgr, err := hypervisor.NewManager(hypervisor.Config{
345-
LibPath: libPath,
346-
Vendor: agent.DetectVendorFromLibPath(libPath),
347-
IsolationMode: getIsolationMode(),
348-
Logger: log.Logger,
349-
StateDir: stateDir,
350-
})
378+
// Use singleton hypervisor manager
379+
hvMgr, err := getHypervisorManager()
351380
if err != nil {
352-
log.Error().Err(err).Msg("Failed to create hypervisor manager")
381+
log.Warn().Err(err).Msg("Failed to get hypervisor manager")
353382
return nil
354383
}
355384

356-
// Start manager to discover devices
357-
if err := hvMgr.Start(); err != nil {
358-
log.Error().Err(err).Msg("Failed to start hypervisor manager")
359-
return nil
360-
}
361-
defer hvMgr.Stop()
362-
363-
// Get devices from hypervisor
364385
devices, err := hvMgr.ListDevices()
365386
if err != nil {
366387
log.Error().Err(err).Msg("Failed to list devices")
@@ -370,40 +391,6 @@ func discoverGPUs() []api.GPUInfo {
370391
return agent.ConvertDevicesToGPUInfo(devices)
371392
}
372393

373-
// createHypervisorManager creates a hypervisor manager for the agent
374-
func createHypervisorManager() (*hypervisor.Manager, error) {
375-
// Check for mock mode
376-
if os.Getenv("GPU_GO_MOCK_GPUS") != "" {
377-
return nil, fmt.Errorf("mock mode enabled, hypervisor not available")
378-
}
379-
380-
libPath := acceleratorLib
381-
if libPath == "" {
382-
libPath = agent.FindAcceleratorLibrary()
383-
}
384-
385-
if libPath == "" {
386-
return nil, fmt.Errorf("accelerator library not found")
387-
}
388-
389-
hvMgr, err := hypervisor.NewManager(hypervisor.Config{
390-
LibPath: libPath,
391-
Vendor: agent.DetectVendorFromLibPath(libPath),
392-
IsolationMode: getIsolationMode(),
393-
Logger: log.Logger,
394-
StateDir: stateDir,
395-
})
396-
if err != nil {
397-
return nil, err
398-
}
399-
400-
if err := hvMgr.Start(); err != nil {
401-
return nil, err
402-
}
403-
404-
return hvMgr, nil
405-
}
406-
407394
func boolToYesNo(b bool) string {
408395
if b {
409396
return "yes"

0 commit comments

Comments
 (0)