feat(federation): add dolt sql-server mode for daemon (bd-wkumz.2)
Add --federation flag to bd daemon start that runs dolt sql-server instead of the embedded driver. Enables multi-writer support and exposes remotesapi on port 8080 for peer-to-peer push/pull. Changes: - Add --federation flag to daemon start command - Create dolt server manager (internal/storage/dolt/server.go) - Update DoltStore to support server mode via MySQL protocol - Integrate server lifecycle into daemon (auto-start/stop) - Add tests for server management and server mode connections Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
committed by
Steve Yegge
parent
458fb7197a
commit
da4584ae57
@@ -17,6 +17,7 @@ import (
|
||||
"github.com/steveyegge/beads/internal/configfile"
|
||||
"github.com/steveyegge/beads/internal/daemon"
|
||||
"github.com/steveyegge/beads/internal/rpc"
|
||||
"github.com/steveyegge/beads/internal/storage/dolt"
|
||||
"github.com/steveyegge/beads/internal/storage/factory"
|
||||
"github.com/steveyegge/beads/internal/storage/sqlite"
|
||||
"github.com/steveyegge/beads/internal/syncbranch"
|
||||
@@ -238,7 +239,8 @@ Run 'bd daemon --help' to see all subcommands.`,
|
||||
fmt.Printf("Logging to: %s\n", logFile)
|
||||
}
|
||||
|
||||
startDaemon(interval, autoCommit, autoPush, autoPull, localMode, foreground, logFile, pidFile, logLevel, logJSON)
|
||||
federation, _ := cmd.Flags().GetBool("federation")
|
||||
startDaemon(interval, autoCommit, autoPush, autoPull, localMode, foreground, logFile, pidFile, logLevel, logJSON, federation)
|
||||
},
|
||||
}
|
||||
|
||||
@@ -264,6 +266,7 @@ func init() {
|
||||
daemonCmd.Flags().Bool("foreground", false, "Run in foreground (don't daemonize)")
|
||||
daemonCmd.Flags().String("log-level", "info", "Log level (debug, info, warn, error)")
|
||||
daemonCmd.Flags().Bool("log-json", false, "Output logs in JSON format (structured logging)")
|
||||
daemonCmd.Flags().Bool("federation", false, "Enable federation mode (runs dolt sql-server with remotesapi)")
|
||||
daemonCmd.Flags().BoolVar(&jsonOutput, "json", false, "Output JSON format")
|
||||
rootCmd.AddCommand(daemonCmd)
|
||||
}
|
||||
@@ -280,7 +283,7 @@ func computeDaemonParentPID() int {
|
||||
}
|
||||
return os.Getppid()
|
||||
}
|
||||
func runDaemonLoop(interval time.Duration, autoCommit, autoPush, autoPull, localMode bool, logPath, pidFile, logLevel string, logJSON bool) {
|
||||
func runDaemonLoop(interval time.Duration, autoCommit, autoPush, autoPull, localMode bool, logPath, pidFile, logLevel string, logJSON, federation bool) {
|
||||
level := parseLogLevel(logLevel)
|
||||
logF, log := setupDaemonLogger(logPath, logJSON, level)
|
||||
defer func() { _ = logF.Close() }()
|
||||
@@ -414,7 +417,49 @@ func runDaemonLoop(interval time.Duration, autoCommit, autoPush, autoPull, local
|
||||
log.Warn("could not remove daemon-error file", "error", err)
|
||||
}
|
||||
|
||||
store, err := factory.NewFromConfig(ctx, beadsDir)
|
||||
// Start dolt sql-server if federation mode is enabled and backend is dolt
|
||||
var doltServer *dolt.Server
|
||||
factoryOpts := factory.Options{}
|
||||
if federation && backend != configfile.BackendDolt {
|
||||
log.Warn("federation mode requires dolt backend, ignoring --federation flag")
|
||||
federation = false
|
||||
}
|
||||
if federation && backend == configfile.BackendDolt {
|
||||
log.Info("starting dolt sql-server for federation mode")
|
||||
|
||||
doltPath := filepath.Join(beadsDir, "dolt")
|
||||
serverLogFile := filepath.Join(beadsDir, "dolt-server.log")
|
||||
|
||||
doltServer = dolt.NewServer(dolt.ServerConfig{
|
||||
DataDir: doltPath,
|
||||
SQLPort: dolt.DefaultSQLPort,
|
||||
RemotesAPIPort: dolt.DefaultRemotesAPIPort,
|
||||
Host: "127.0.0.1",
|
||||
LogFile: serverLogFile,
|
||||
})
|
||||
|
||||
if err := doltServer.Start(ctx); err != nil {
|
||||
log.Error("failed to start dolt sql-server", "error", err)
|
||||
return
|
||||
}
|
||||
defer func() {
|
||||
log.Info("stopping dolt sql-server")
|
||||
if err := doltServer.Stop(); err != nil {
|
||||
log.Warn("error stopping dolt sql-server", "error", err)
|
||||
}
|
||||
}()
|
||||
|
||||
log.Info("dolt sql-server started",
|
||||
"sql_port", doltServer.SQLPort(),
|
||||
"remotesapi_port", doltServer.RemotesAPIPort())
|
||||
|
||||
// Configure factory to use server mode
|
||||
factoryOpts.ServerMode = true
|
||||
factoryOpts.ServerHost = doltServer.Host()
|
||||
factoryOpts.ServerPort = doltServer.SQLPort()
|
||||
}
|
||||
|
||||
store, err := factory.NewFromConfigWithOptions(ctx, beadsDir, factoryOpts)
|
||||
if err != nil {
|
||||
log.Error("cannot open database", "error", err)
|
||||
return // Use return instead of os.Exit to allow defers to run
|
||||
@@ -427,8 +472,10 @@ func runDaemonLoop(interval time.Duration, autoCommit, autoPush, autoPull, local
|
||||
if sqliteStore, ok := store.(*sqlite.SQLiteStorage); ok {
|
||||
sqliteStore.EnableFreshnessChecking()
|
||||
log.Info("database opened", "path", store.Path(), "backend", "sqlite", "freshness_checking", true)
|
||||
} else if federation {
|
||||
log.Info("database opened", "path", store.Path(), "backend", "dolt", "mode", "federation/server")
|
||||
} else {
|
||||
log.Info("database opened", "path", store.Path(), "backend", "dolt")
|
||||
log.Info("database opened", "path", store.Path(), "backend", "dolt", "mode", "embedded")
|
||||
}
|
||||
|
||||
// Auto-upgrade .beads/.gitignore if outdated
|
||||
|
||||
@@ -369,7 +369,7 @@ func stopAllDaemons() {
|
||||
}
|
||||
|
||||
// startDaemon starts the daemon (in foreground if requested, otherwise background)
|
||||
func startDaemon(interval time.Duration, autoCommit, autoPush, autoPull, localMode, foreground bool, logFile, pidFile, logLevel string, logJSON bool) {
|
||||
func startDaemon(interval time.Duration, autoCommit, autoPush, autoPull, localMode, foreground bool, logFile, pidFile, logLevel string, logJSON, federation bool) {
|
||||
logPath, err := getLogFilePath(logFile)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
|
||||
@@ -385,7 +385,7 @@ func startDaemon(interval time.Duration, autoCommit, autoPush, autoPull, localMo
|
||||
|
||||
// Run in foreground if --foreground flag set or if we're the forked child process
|
||||
if foreground || os.Getenv("BD_DAEMON_FOREGROUND") == "1" {
|
||||
runDaemonLoop(interval, autoCommit, autoPush, autoPull, localMode, logPath, pidFile, logLevel, logJSON)
|
||||
runDaemonLoop(interval, autoCommit, autoPush, autoPull, localMode, logPath, pidFile, logLevel, logJSON, federation)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -419,6 +419,9 @@ func startDaemon(interval time.Duration, autoCommit, autoPush, autoPull, localMo
|
||||
if logJSON {
|
||||
args = append(args, "--log-json")
|
||||
}
|
||||
if federation {
|
||||
args = append(args, "--federation")
|
||||
}
|
||||
|
||||
cmd := exec.Command(exe, args...) // #nosec G204 - bd daemon command from trusted binary
|
||||
cmd.Env = append(os.Environ(), "BD_DAEMON_FOREGROUND=1")
|
||||
|
||||
@@ -23,12 +23,18 @@ The daemon will:
|
||||
- Pull remote changes periodically
|
||||
- Auto-import when remote changes detected
|
||||
|
||||
Federation mode (--federation):
|
||||
- Starts dolt sql-server for multi-writer support
|
||||
- Exposes remotesapi on port 8080 for peer-to-peer push/pull
|
||||
- Enables real-time sync between Gas Towns
|
||||
|
||||
Examples:
|
||||
bd daemon start # Start with defaults
|
||||
bd daemon start --auto-commit # Enable auto-commit
|
||||
bd daemon start --auto-push # Enable auto-push (implies --auto-commit)
|
||||
bd daemon start --foreground # Run in foreground (for systemd/supervisord)
|
||||
bd daemon start --local # Local-only mode (no git sync)`,
|
||||
bd daemon start --local # Local-only mode (no git sync)
|
||||
bd daemon start --federation # Enable federation mode (dolt sql-server)`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
interval, _ := cmd.Flags().GetDuration("interval")
|
||||
autoCommit, _ := cmd.Flags().GetBool("auto-commit")
|
||||
@@ -39,6 +45,7 @@ Examples:
|
||||
foreground, _ := cmd.Flags().GetBool("foreground")
|
||||
logLevel, _ := cmd.Flags().GetString("log-level")
|
||||
logJSON, _ := cmd.Flags().GetBool("log-json")
|
||||
federation, _ := cmd.Flags().GetBool("federation")
|
||||
|
||||
// NOTE: Only load daemon auto-settings from the database in foreground mode.
|
||||
//
|
||||
@@ -136,6 +143,8 @@ Examples:
|
||||
// Start daemon
|
||||
if localMode {
|
||||
fmt.Printf("Starting bd daemon in LOCAL mode (interval: %v, no git sync)\n", interval)
|
||||
} else if federation {
|
||||
fmt.Printf("Starting bd daemon in FEDERATION mode (interval: %v, dolt sql-server with remotesapi)\n", interval)
|
||||
} else {
|
||||
fmt.Printf("Starting bd daemon (interval: %v, auto-commit: %v, auto-push: %v, auto-pull: %v)\n",
|
||||
interval, autoCommit, autoPush, autoPull)
|
||||
@@ -144,7 +153,7 @@ Examples:
|
||||
fmt.Printf("Logging to: %s\n", logFile)
|
||||
}
|
||||
|
||||
startDaemon(interval, autoCommit, autoPush, autoPull, localMode, foreground, logFile, pidFile, logLevel, logJSON)
|
||||
startDaemon(interval, autoCommit, autoPush, autoPull, localMode, foreground, logFile, pidFile, logLevel, logJSON, federation)
|
||||
},
|
||||
}
|
||||
|
||||
@@ -158,4 +167,5 @@ func init() {
|
||||
daemonStartCmd.Flags().Bool("foreground", false, "Run in foreground (don't daemonize)")
|
||||
daemonStartCmd.Flags().String("log-level", "info", "Log level (debug, info, warn, error)")
|
||||
daemonStartCmd.Flags().Bool("log-json", false, "Output logs in JSON format")
|
||||
daemonStartCmd.Flags().Bool("federation", false, "Enable federation mode (runs dolt sql-server with remotesapi on port 8080)")
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user