Files
beads/internal/storage/factory/factory.go
mayor 13c362e67e 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 <noreply@anthropic.com>
2026-01-23 22:35:34 -08:00

123 lines
4.2 KiB
Go

// Package factory provides functions for creating storage backends based on configuration.
package factory
import (
"context"
"fmt"
"time"
"github.com/steveyegge/beads/internal/configfile"
"github.com/steveyegge/beads/internal/storage"
"github.com/steveyegge/beads/internal/storage/sqlite"
)
// BackendFactory is a function that creates a storage backend
type BackendFactory func(ctx context.Context, path string, opts Options) (storage.Storage, error)
// backendRegistry holds registered backend factories
var backendRegistry = make(map[string]BackendFactory)
// RegisterBackend registers a storage backend factory
func RegisterBackend(name string, factory BackendFactory) {
backendRegistry[name] = factory
}
// Options configures how the storage backend is opened
type Options struct {
ReadOnly bool
LockTimeout time.Duration
// 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: 3307)
ServerUser string // MySQL user (default: root)
Database string // Database name for Dolt server mode (default: beads)
}
// New creates a storage backend based on the backend type.
// For SQLite, path should be the full path to the .db file.
// For Dolt, path should be the directory containing the Dolt database.
func New(ctx context.Context, backend, path string) (storage.Storage, error) {
return NewWithOptions(ctx, backend, path, Options{})
}
// NewWithOptions creates a storage backend with the specified options.
func NewWithOptions(ctx context.Context, backend, path string, opts Options) (storage.Storage, error) {
switch backend {
case configfile.BackendSQLite, "":
if opts.ReadOnly {
if opts.LockTimeout > 0 {
return sqlite.NewReadOnlyWithTimeout(ctx, path, opts.LockTimeout)
}
return sqlite.NewReadOnly(ctx, path)
}
if opts.LockTimeout > 0 {
return sqlite.NewWithTimeout(ctx, path, opts.LockTimeout)
}
return sqlite.New(ctx, path)
default:
// Check if backend is registered (e.g., dolt with CGO)
if factory, ok := backendRegistry[backend]; ok {
return factory(ctx, path, opts)
}
// Provide helpful error for dolt on systems without CGO
if backend == configfile.BackendDolt {
return nil, fmt.Errorf("dolt backend requires CGO (not available on this build); use sqlite backend or install from pre-built binaries")
}
return nil, fmt.Errorf("unknown storage backend: %s (supported: sqlite, dolt)", backend)
}
}
// NewFromConfig creates a storage backend based on the metadata.json configuration.
// beadsDir is the path to the .beads directory.
func NewFromConfig(ctx context.Context, beadsDir string) (storage.Storage, error) {
return NewFromConfigWithOptions(ctx, beadsDir, Options{})
}
// NewFromConfigWithOptions creates a storage backend with options from metadata.json.
func NewFromConfigWithOptions(ctx context.Context, beadsDir string, opts Options) (storage.Storage, error) {
cfg, err := configfile.Load(beadsDir)
if err != nil {
return nil, fmt.Errorf("loading config: %w", err)
}
if cfg == nil {
cfg = configfile.DefaultConfig()
}
backend := cfg.GetBackend()
switch backend {
case configfile.BackendSQLite:
return NewWithOptions(ctx, backend, cfg.DatabasePath(beadsDir), opts)
case configfile.BackendDolt:
// Merge Dolt server mode config into options (config provides defaults, opts can override)
if cfg.IsDoltServerMode() {
opts.ServerMode = true
if opts.ServerHost == "" {
opts.ServerHost = cfg.GetDoltServerHost()
}
if opts.ServerPort == 0 {
opts.ServerPort = cfg.GetDoltServerPort()
}
if opts.ServerUser == "" {
opts.ServerUser = cfg.GetDoltServerUser()
}
if opts.Database == "" {
opts.Database = cfg.GetDoltDatabase()
}
}
return NewWithOptions(ctx, backend, cfg.DatabasePath(beadsDir), opts)
default:
return nil, fmt.Errorf("unknown storage backend in config: %s", backend)
}
}
// GetBackendFromConfig returns the backend type from metadata.json
func GetBackendFromConfig(beadsDir string) string {
cfg, err := configfile.Load(beadsDir)
if err != nil || cfg == nil {
return configfile.BackendSQLite
}
return cfg.GetBackend()
}