- 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>
116 lines
3.0 KiB
Go
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
|
|
}
|
|
}
|
|
}
|