readysite / pkg / database / engines / remote.go
1.8 KB
remote.go
package engines

import (
	"database/sql"
	"log"
	"time"

	"github.com/readysite/readysite/pkg/database"
	"github.com/tursodatabase/go-libsql"
)

// RemoteOption configures a remote database
type RemoteOption func(*remoteConfig)

type remoteConfig struct {
	syncInterval time.Duration
}

// WithSyncInterval sets the automatic sync interval.
// If not set, sync must be called manually.
func WithSyncInterval(d time.Duration) RemoteOption {
	return func(c *remoteConfig) {
		c.syncInterval = d
	}
}

// NewRemote creates a new embedded replica database that syncs with a remote libSQL server.
// It maintains a local copy for fast reads while syncing writes to the primary.
//
// The localPath is where the local replica is stored.
// The primaryURL is the remote libSQL server URL (e.g., "libsql://db-name.turso.io").
// The authToken is the authentication token for the remote server.
//
// Example:
//
//	db := engines.NewRemote(
//	    "./data/replica.db",
//	    os.Getenv("DB_URL"),
//	    os.Getenv("DB_TOKEN"),
//	    engines.WithSyncInterval(time.Minute),
//	)
func NewRemote(localPath, primaryURL, authToken string, opts ...RemoteOption) *database.Database {
	cfg := &remoteConfig{}
	for _, opt := range opts {
		opt(cfg)
	}

	// Build libsql options
	libsqlOpts := []libsql.Option{
		libsql.WithAuthToken(authToken),
	}

	if cfg.syncInterval > 0 {
		libsqlOpts = append(libsqlOpts, libsql.WithSyncInterval(cfg.syncInterval))
	}

	connector, err := libsql.NewEmbeddedReplicaConnector(
		localPath,
		primaryURL,
		libsqlOpts...,
	)
	if err != nil {
		log.Fatal("Failed to create database connector:", err)
	}

	db := sql.OpenDB(connector)

	return &database.Database{DB: db, Sync: syncer{connector}}
}

type syncer struct {
	connector *libsql.Connector
}

func (s syncer) Sync() error {
	_, err := s.connector.Sync()
	return err
}
← Back