From 13c362e67eed912139f3459f82f59211508e3b1d Mon Sep 17 00:00:00 2001 From: mayor Date: Fri, 23 Jan 2026 22:33:55 -0800 Subject: [PATCH] feat(dolt): add server mode support for multi-client access - Add dolt_mode, dolt_server_host, dolt_server_port fields to configfile.Config - Add IsDoltServerMode(), GetDoltServerHost(), GetDoltServerPort() helpers - Update factory to read server mode config and set Options accordingly - Skip bootstrap in server mode (database lives on server) - Pass Database name to dolt.Config for USE statement after connecting - Disable dolt stats collection to avoid lock issues in embedded mode This enables bd to connect to a running dolt sql-server (started via 'gt dolt start') instead of using embedded mode, allowing multi-client access without file locking conflicts. Co-Authored-By: Claude Opus 4.5 --- cmd/bd/main.go | 12 ++++++++++++ internal/configfile/configfile.go | 10 +++++----- internal/storage/dolt/store.go | 5 +++++ internal/storage/factory/factory.go | 2 +- 4 files changed, 23 insertions(+), 6 deletions(-) diff --git a/cmd/bd/main.go b/cmd/bd/main.go index ad779fdf..7ddda083 100644 --- a/cmd/bd/main.go +++ b/cmd/bd/main.go @@ -838,6 +838,18 @@ var rootCmd = &cobra.Command{ if backend == configfile.BackendDolt { // For Dolt, use the dolt subdirectory doltPath := filepath.Join(beadsDir, "dolt") + + // Check if server mode is configured in metadata.json + cfg, cfgErr := configfile.Load(beadsDir) + if cfgErr == nil && cfg != nil && cfg.IsDoltServerMode() { + opts.ServerMode = true + opts.ServerHost = cfg.GetDoltServerHost() + opts.ServerPort = cfg.GetDoltServerPort() + if cfg.Database != "" { + opts.Database = cfg.Database + } + } + store, err = factory.NewWithOptions(rootCtx, backend, doltPath, opts) } else { // SQLite backend diff --git a/internal/configfile/configfile.go b/internal/configfile/configfile.go index 218ba03e..99bea938 100644 --- a/internal/configfile/configfile.go +++ b/internal/configfile/configfile.go @@ -23,7 +23,7 @@ type Config struct { // This enables multi-writer access for multi-agent environments. DoltMode string `json:"dolt_mode,omitempty"` // "embedded" (default) or "server" DoltServerHost string `json:"dolt_server_host,omitempty"` // Server host (default: 127.0.0.1) - DoltServerPort int `json:"dolt_server_port,omitempty"` // Server port (default: 3306) + DoltServerPort int `json:"dolt_server_port,omitempty"` // Server port (default: 3307) DoltServerUser string `json:"dolt_server_user,omitempty"` // MySQL user (default: root) // Note: Password should be set via BEADS_DOLT_PASSWORD env var for security @@ -222,13 +222,13 @@ const ( // Default Dolt server settings const ( DefaultDoltServerHost = "127.0.0.1" - DefaultDoltServerPort = 3306 + DefaultDoltServerPort = 3307 // Use 3307 to avoid conflict with MySQL on 3306 DefaultDoltServerUser = "root" ) // IsDoltServerMode returns true if Dolt is configured for server mode. func (c *Config) IsDoltServerMode() bool { - return c.GetBackend() == BackendDolt && c.DoltMode == DoltModeServer + return c.GetBackend() == BackendDolt && strings.ToLower(c.DoltMode) == DoltModeServer } // GetDoltMode returns the Dolt connection mode, defaulting to embedded. @@ -247,9 +247,9 @@ func (c *Config) GetDoltServerHost() string { return c.DoltServerHost } -// GetDoltServerPort returns the Dolt server port, defaulting to 3306. +// GetDoltServerPort returns the Dolt server port, defaulting to 3307. func (c *Config) GetDoltServerPort() int { - if c.DoltServerPort == 0 { + if c.DoltServerPort <= 0 { return DefaultDoltServerPort } return c.DoltServerPort diff --git a/internal/storage/dolt/store.go b/internal/storage/dolt/store.go index 2fed6d7e..f28a8f00 100644 --- a/internal/storage/dolt/store.go +++ b/internal/storage/dolt/store.go @@ -262,6 +262,11 @@ func openEmbeddedConnection(ctx context.Context, cfg *Config) (*sql.DB, string, return nil, "", fmt.Errorf("failed to connect to Dolt database after %d retries: %w", cfg.LockRetries, lastErr) } + // Disable statistics collection to avoid stats subdatabase lock issues + // The stats database can cause "cannot update manifest: database is read only" + // errors when multiple processes access the embedded Dolt database + _, _ = db.ExecContext(ctx, "SET @@dolt_stats_enabled = 0") + return db, connStr, nil } diff --git a/internal/storage/factory/factory.go b/internal/storage/factory/factory.go index 4bc37870..627d1fc0 100644 --- a/internal/storage/factory/factory.go +++ b/internal/storage/factory/factory.go @@ -30,7 +30,7 @@ type Options struct { // Dolt server mode options (federation) ServerMode bool // Connect to dolt sql-server instead of embedded ServerHost string // Server host (default: 127.0.0.1) - ServerPort int // Server port (default: 3306) + ServerPort int // Server port (default: 3307) ServerUser string // MySQL user (default: root) Database string // Database name for Dolt server mode (default: beads) }