Files
beads/cmd/bd/daemon_server.go
Steve Yegge eb00ab8005 Refactor daemon.go for testability and maintainability (bd-2b34)
- Split 1567-line daemon.go into 5 focused modules
- daemon_config.go (122 lines): config/path resolution
- daemon_lifecycle.go (451 lines): start/stop/status/health/metrics
- daemon_sync.go (510 lines): export/import/sync logic
- daemon_server.go (115 lines): RPC server setup
- daemon_logger.go (43 lines): logging utilities
- Reduced main daemon.go to 389 lines (75% reduction)
- All tests pass, improved code organization

Amp-Thread-ID: https://ampcode.com/threads/T-7504c501-f962-4b82-a6d9-8e33f547757d
Co-authored-by: Amp <amp@ampcode.com>
2025-11-01 19:20:01 -07:00

116 lines
3.0 KiB
Go

package main
import (
"context"
"os"
"os/signal"
"path/filepath"
"time"
"github.com/steveyegge/beads/internal/rpc"
"github.com/steveyegge/beads/internal/storage"
)
// startRPCServer initializes and starts the RPC server
func startRPCServer(ctx context.Context, socketPath string, store storage.Storage, workspacePath string, dbPath string, log daemonLogger) (*rpc.Server, chan error, error) {
// Sync daemon version with CLI version
rpc.ServerVersion = Version
server := rpc.NewServer(socketPath, store, workspacePath, dbPath)
serverErrChan := make(chan error, 1)
go func() {
log.log("Starting RPC server: %s", socketPath)
if err := server.Start(ctx); err != nil {
log.log("RPC server error: %v", err)
serverErrChan <- err
}
}()
select {
case err := <-serverErrChan:
log.log("RPC server failed to start: %v", err)
return nil, nil, err
case <-server.WaitReady():
log.log("RPC server ready (socket listening)")
case <-time.After(5 * time.Second):
log.log("WARNING: Server didn't signal ready after 5 seconds (may still be starting)")
}
return server, serverErrChan, nil
}
// runGlobalDaemon runs the global routing daemon
func runGlobalDaemon(log daemonLogger) {
globalDir, err := getGlobalBeadsDir()
if err != nil {
log.log("Error: cannot get global beads directory: %v", err)
os.Exit(1)
}
socketPath := filepath.Join(globalDir, "bd.sock")
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
server, _, err := startRPCServer(ctx, socketPath, nil, globalDir, "", log)
if err != nil {
return
}
sigChan := make(chan os.Signal, 1)
signal.Notify(sigChan, daemonSignals...)
defer signal.Stop(sigChan)
sig := <-sigChan
log.log("Received signal: %v", sig)
log.log("Shutting down global daemon...")
cancel()
if err := server.Stop(); err != nil {
log.log("Error stopping server: %v", err)
}
log.log("Global daemon stopped")
}
// runEventLoop runs the daemon event loop (polling mode)
func runEventLoop(ctx context.Context, cancel context.CancelFunc, ticker *time.Ticker, doSync func(), server *rpc.Server, serverErrChan chan error, log daemonLogger) {
sigChan := make(chan os.Signal, 1)
signal.Notify(sigChan, daemonSignals...)
defer signal.Stop(sigChan)
for {
select {
case <-ticker.C:
if ctx.Err() != nil {
return
}
doSync()
case sig := <-sigChan:
if isReloadSignal(sig) {
log.log("Received reload signal, ignoring (daemon continues running)")
continue
}
log.log("Received signal %v, shutting down gracefully...", sig)
cancel()
if err := server.Stop(); err != nil {
log.log("Error stopping RPC server: %v", err)
}
return
case <-ctx.Done():
log.log("Context canceled, shutting down")
if err := server.Stop(); err != nil {
log.log("Error stopping RPC server: %v", err)
}
return
case err := <-serverErrChan:
log.log("RPC server failed: %v", err)
cancel()
if err := server.Stop(); err != nil {
log.log("Error stopping RPC server: %v", err)
}
return
}
}
}