mirror of
https://github.com/dolthub/dolt.git
synced 2026-01-08 08:49:52 -06:00
go/libraries/doltcore/env: Move the relevant *DBLock stuff into cmd/commands/sqlserver as LocalCreds.
This commit is contained in:
@@ -812,7 +812,7 @@ func addCreateDatabaseHeader(dEnv *env.DoltEnv, fPath, dbName string) errhand.Ve
|
||||
// TODO: find a more elegant way to get database name, possibly implement a method in DoltEnv
|
||||
// getActiveDatabaseName returns the name of the current active database
|
||||
func getActiveDatabaseName(ctx context.Context, dEnv *env.DoltEnv) (string, errhand.VerboseError) {
|
||||
mrEnv, err := env.MultiEnvForDirectory(ctx, dEnv.Config.WriteableConfig(), dEnv.FS, dEnv.Version, dEnv.IgnoreLockFile, dEnv)
|
||||
mrEnv, err := env.MultiEnvForDirectory(ctx, dEnv.Config.WriteableConfig(), dEnv.FS, dEnv.Version, dEnv)
|
||||
if err != nil {
|
||||
return "", errhand.VerboseErrorFromError(err)
|
||||
}
|
||||
|
||||
@@ -407,7 +407,7 @@ func doltSessionFactory(pro *dsqle.DoltDatabaseProvider, config config.ReadWrite
|
||||
// NewSqlEngineForEnv returns a SqlEngine configured for the environment provided, with a single root user.
|
||||
// Returns the new engine, the first database name, and any error that occurred.
|
||||
func NewSqlEngineForEnv(ctx context.Context, dEnv *env.DoltEnv) (*SqlEngine, string, error) {
|
||||
mrEnv, err := env.MultiEnvForDirectory(ctx, dEnv.Config.WriteableConfig(), dEnv.FS, dEnv.Version, dEnv.IgnoreLockFile, dEnv)
|
||||
mrEnv, err := env.MultiEnvForDirectory(ctx, dEnv.Config.WriteableConfig(), dEnv.FS, dEnv.Version, dEnv)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
|
||||
@@ -104,9 +104,7 @@ func (cmd FilterBranchCmd) Exec(ctx context.Context, commandStr string, args []s
|
||||
return HandleVErrAndExitCode(verr, usage)
|
||||
}
|
||||
|
||||
if dEnv.IsLocked() {
|
||||
return HandleVErrAndExitCode(errhand.VerboseErrorFromError(env.ErrActiveServerLock.New(dEnv.LockFile())), help)
|
||||
}
|
||||
// TODO: Assert that dEnv.DoltDB.AccessMode() != ReadOnly
|
||||
|
||||
queryString := apr.GetValueOrDefault(QueryFlag, "")
|
||||
verbose := apr.Contains(cli.VerboseFlag)
|
||||
@@ -281,7 +279,7 @@ func rebaseSqlEngine(ctx context.Context, dEnv *env.DoltEnv, cm *doltdb.Commit)
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
mrEnv, err := env.MultiEnvForDirectory(ctx, dEnv.Config.WriteableConfig(), dEnv.FS, dEnv.Version, dEnv.IgnoreLockFile, dEnv)
|
||||
mrEnv, err := env.MultiEnvForDirectory(ctx, dEnv.Config.WriteableConfig(), dEnv.FS, dEnv.Version, dEnv)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
@@ -88,9 +88,7 @@ func (cmd GarbageCollectionCmd) Exec(ctx context.Context, commandStr string, arg
|
||||
help, usage := cli.HelpAndUsagePrinters(cli.CommandDocsForCommandString(commandStr, gcDocs, ap))
|
||||
apr := cli.ParseArgsOrDie(ap, args, help)
|
||||
|
||||
if dEnv.IsLocked() {
|
||||
return HandleVErrAndExitCode(errhand.VerboseErrorFromError(env.ErrActiveServerLock.New(dEnv.LockFile())), help)
|
||||
}
|
||||
// TODO: Assert that dEnv.DoltDB.AccessMode() != ReadOnly?
|
||||
|
||||
var err error
|
||||
if apr.Contains(cli.ShallowFlag) {
|
||||
|
||||
@@ -20,7 +20,6 @@ import (
|
||||
"github.com/dolthub/go-mysql-server/sql"
|
||||
|
||||
"github.com/dolthub/dolt/go/cmd/dolt/cli"
|
||||
"github.com/dolthub/dolt/go/cmd/dolt/commands"
|
||||
"github.com/dolthub/dolt/go/cmd/dolt/errhand"
|
||||
"github.com/dolthub/dolt/go/libraries/doltcore/env"
|
||||
"github.com/dolthub/dolt/go/libraries/doltcore/table/editor"
|
||||
@@ -72,9 +71,7 @@ func (cmd RebuildCmd) Exec(ctx context.Context, commandStr string, args []string
|
||||
return HandleErr(errhand.BuildDError("Both the table and index names must be provided.").Build(), usage)
|
||||
}
|
||||
|
||||
if dEnv.IsLocked() {
|
||||
return commands.HandleVErrAndExitCode(errhand.VerboseErrorFromError(env.ErrActiveServerLock.New(dEnv.LockFile())), usage)
|
||||
}
|
||||
// TODO: Error if dEnv.DoltDB.AccessMode() == ReadOnly?
|
||||
|
||||
working, err := dEnv.WorkingRoot(context.Background())
|
||||
if err != nil {
|
||||
|
||||
@@ -170,9 +170,7 @@ func (cmd ImportCmd) Exec(ctx context.Context, commandStr string, args []string,
|
||||
return 1
|
||||
}
|
||||
|
||||
if dEnv.IsLocked() {
|
||||
return commands.HandleVErrAndExitCode(errhand.VerboseErrorFromError(env.ErrActiveServerLock.New(dEnv.LockFile())), usage)
|
||||
}
|
||||
// TODO: Error if dEnv.DoltDB.AccessMode() == ReadOnly?
|
||||
|
||||
return commands.HandleVErrAndExitCode(importSchema(ctx, dEnv, apr), usage)
|
||||
}
|
||||
|
||||
127
go/cmd/dolt/commands/sqlserver/creds.go
Normal file
127
go/cmd/dolt/commands/sqlserver/creds.go
Normal file
@@ -0,0 +1,127 @@
|
||||
// Copyright 2023 Dolthub, Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package sqlserver
|
||||
|
||||
import (
|
||||
"os"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/google/uuid"
|
||||
|
||||
"github.com/dolthub/dolt/go/libraries/doltcore/dbfactory"
|
||||
"github.com/dolthub/dolt/go/libraries/utils/filesys"
|
||||
)
|
||||
|
||||
const ServerLocalCredsFile = "sql-server.lock"
|
||||
|
||||
// LocalCreds is a struct that contains information about how to access the
|
||||
// locally running server for a CLI process which wants to operate against a
|
||||
// database while a sql-server process is running. It contains the pid of the
|
||||
// process that created the lockfile, the port that the server is running on
|
||||
// and the password to be used connecting to the server.
|
||||
//
|
||||
// Pid is a legacy field which is retained for compatibility but no longer
|
||||
// influences the behavior of the running program(s).
|
||||
type LocalCreds struct {
|
||||
Pid int
|
||||
Port int
|
||||
Secret string
|
||||
}
|
||||
|
||||
func NewLocalCreds(port int) *LocalCreds {
|
||||
return &LocalCreds{os.Getpid(), port, uuid.New().String()}
|
||||
}
|
||||
|
||||
// Best effort attempt to remove local creds file persisted as the Filesys
|
||||
// rooted there.
|
||||
func RemoveLocalCreds(fs filesys.Filesys) {
|
||||
credsFilePath, err := fs.Abs(filepath.Join(dbfactory.DoltDir, ServerLocalCredsFile))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
_ = fs.Delete(credsFilePath, false)
|
||||
}
|
||||
|
||||
// WriteLocalCreds writes a file containing the contents of LocalCreds to the
|
||||
// DoltDir rooted at the provided Filesys.
|
||||
func WriteLocalCreds(fs filesys.Filesys, creds *LocalCreds) error {
|
||||
// if the DoltDir doesn't exist, create it.
|
||||
doltDir, err := fs.Abs(dbfactory.DoltDir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = fs.MkDirs(doltDir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
credsFile, err := fs.Abs(filepath.Join(dbfactory.DoltDir, ServerLocalCredsFile))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
portStr := strconv.Itoa(creds.Port)
|
||||
if creds.Port < 0 {
|
||||
portStr = "-"
|
||||
}
|
||||
|
||||
return fs.WriteFile(credsFile, []byte(fmt.Sprintf("%d:%s:%s", creds.Pid, portStr, creds.Secret)), 0600)
|
||||
}
|
||||
|
||||
func LoadLocalCreds(fs filesys.Filesys, credsFilePath string) (creds *LocalCreds, err error) {
|
||||
rd, err := fs.OpenForRead(credsFilePath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rd.Close()
|
||||
|
||||
b := make([]byte, 256)
|
||||
n, err := rd.Read(b)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
data := strings.TrimSpace(string(b[:n]))
|
||||
|
||||
parts := strings.Split(data, ":")
|
||||
if len(parts) == 1 {
|
||||
// Legacy Lock file. We can remove this code path in a couple of months (6/2023)
|
||||
pid, err := strconv.Atoi(parts[0])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &LocalCreds{Pid: pid, Port: -1, Secret: ""}, nil
|
||||
}
|
||||
if len(parts) != 3 {
|
||||
return nil, fmt.Errorf("invalid lock file format")
|
||||
}
|
||||
|
||||
pid, err := strconv.Atoi(parts[0])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
port := -1
|
||||
if parts[1] != "-" {
|
||||
port, err = strconv.Atoi(parts[1])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
secret := parts[2]
|
||||
return &LocalCreds{Pid: pid, Port: port, Secret: secret}, nil
|
||||
}
|
||||
@@ -140,7 +140,7 @@ func Serve(
|
||||
var mrEnv *env.MultiRepoEnv
|
||||
InitMultiEnv := &svcs.AnonService{
|
||||
InitF: func(ctx context.Context) (err error) {
|
||||
mrEnv, err = env.MultiEnvForDirectory(ctx, dEnv.Config.WriteableConfig(), fs, dEnv.Version, dEnv.IgnoreLockFile, dEnv)
|
||||
mrEnv, err = env.MultiEnvForDirectory(ctx, dEnv.Config.WriteableConfig(), fs, dEnv.Version, dEnv)
|
||||
return err
|
||||
},
|
||||
}
|
||||
@@ -154,18 +154,18 @@ func Serve(
|
||||
}
|
||||
controller.Register(AssertNoDatabasesInAccessModeReadOnly)
|
||||
|
||||
var serverLock *env.DBLock
|
||||
InitGlobalServerLock := &svcs.AnonService{
|
||||
var localCreds *LocalCreds
|
||||
InitServerLocalCreds := &svcs.AnonService{
|
||||
InitF: func(context.Context) (err error) {
|
||||
serverLock, err = acquireGlobalSqlServerLock(serverConfig.Port(), dEnv)
|
||||
localCreds, err = persistServerLocalCreds(serverConfig.Port(), dEnv)
|
||||
return err
|
||||
},
|
||||
StopF: func() error {
|
||||
dEnv.FS.Delete(dEnv.LockFile(), false)
|
||||
RemoveLocalCreds(dEnv.FS)
|
||||
return nil
|
||||
},
|
||||
}
|
||||
controller.Register(InitGlobalServerLock)
|
||||
controller.Register(InitServerLocalCreds)
|
||||
|
||||
var clusterController *cluster.Controller
|
||||
InitClusterController := &svcs.AnonService{
|
||||
@@ -283,7 +283,7 @@ func Serve(
|
||||
InitF: func(context.Context) error {
|
||||
mysqlDb := sqlEngine.GetUnderlyingEngine().Analyzer.Catalog.MySQLDb
|
||||
ed := mysqlDb.Editor()
|
||||
mysqlDb.AddSuperUser(ed, LocalConnectionUser, "localhost", serverLock.Secret)
|
||||
mysqlDb.AddSuperUser(ed, LocalConnectionUser, "localhost", localCreds.Secret)
|
||||
ed.Close()
|
||||
return nil
|
||||
},
|
||||
@@ -555,26 +555,13 @@ func Serve(
|
||||
return nil, controller.WaitForStop()
|
||||
}
|
||||
|
||||
// acquireGlobalSqlServerLock attempts to acquire a global lock on the SQL server. If no error is returned, then the lock was acquired.
|
||||
func acquireGlobalSqlServerLock(port int, dEnv *env.DoltEnv) (*env.DBLock, error) {
|
||||
locked, _, err := dEnv.GetLock()
|
||||
func persistServerLocalCreds(port int, dEnv *env.DoltEnv) (*LocalCreds, error) {
|
||||
creds := NewLocalCreds(port)
|
||||
err := WriteLocalCreds(dEnv.FS, creds)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if locked {
|
||||
lockPath := dEnv.LockFile()
|
||||
err = fmt.Errorf("Database locked by another sql-server; Lock file: %s", lockPath)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
lck := env.NewDBLock(port)
|
||||
err = dEnv.Lock(&lck)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("Server can not start. Failed to acquire lock: %s", err.Error())
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &lck, nil
|
||||
return creds, err
|
||||
}
|
||||
|
||||
// remotesapiAuth facilitates the implementation remotesrv.AccessControl for the remotesapi server.
|
||||
|
||||
@@ -71,9 +71,8 @@ func (cmd StashClearCmd) Exec(ctx context.Context, commandStr string, args []str
|
||||
ap := cmd.ArgParser()
|
||||
help, usage := cli.HelpAndUsagePrinters(cli.CommandDocsForCommandString(commandStr, stashClearDocs, ap))
|
||||
apr := cli.ParseArgsOrDie(ap, args, help)
|
||||
if dEnv.IsLocked() {
|
||||
return commands.HandleVErrAndExitCode(errhand.VerboseErrorFromError(env.ErrActiveServerLock.New(dEnv.LockFile())), help)
|
||||
}
|
||||
|
||||
// TODO: Error if dEnv.DoltDB.AccessMode() == ReadOnly?
|
||||
|
||||
if apr.NArg() != 0 {
|
||||
usage()
|
||||
|
||||
@@ -74,9 +74,8 @@ func (cmd StashDropCmd) Exec(ctx context.Context, commandStr string, args []stri
|
||||
ap := cmd.ArgParser()
|
||||
help, usage := cli.HelpAndUsagePrinters(cli.CommandDocsForCommandString(commandStr, stashDropDocs, ap))
|
||||
apr := cli.ParseArgsOrDie(ap, args, help)
|
||||
if dEnv.IsLocked() {
|
||||
return commands.HandleVErrAndExitCode(errhand.VerboseErrorFromError(env.ErrActiveServerLock.New(dEnv.LockFile())), help)
|
||||
}
|
||||
|
||||
// TODO: Error if dEnv.DoltDB.AccessMode() == ReadOnly?
|
||||
|
||||
var idx = 0
|
||||
var err error
|
||||
|
||||
@@ -71,9 +71,8 @@ func (cmd StashListCmd) Exec(ctx context.Context, commandStr string, args []stri
|
||||
ap := cmd.ArgParser()
|
||||
help, usage := cli.HelpAndUsagePrinters(cli.CommandDocsForCommandString(commandStr, stashListDocs, ap))
|
||||
cli.ParseArgsOrDie(ap, args, help)
|
||||
if dEnv.IsLocked() {
|
||||
return commands.HandleVErrAndExitCode(errhand.VerboseErrorFromError(env.ErrActiveServerLock.New(dEnv.LockFile())), help)
|
||||
}
|
||||
|
||||
// TODO: Error if dEnv.DoltDB.AccessMode() == ReadOnly?
|
||||
|
||||
err := listStashes(ctx, dEnv)
|
||||
if err != nil {
|
||||
|
||||
@@ -80,9 +80,8 @@ func (cmd StashPopCmd) Exec(ctx context.Context, commandStr string, args []strin
|
||||
ap := cmd.ArgParser()
|
||||
help, usage := cli.HelpAndUsagePrinters(cli.CommandDocsForCommandString(commandStr, stashPopDocs, ap))
|
||||
apr := cli.ParseArgsOrDie(ap, args, help)
|
||||
if dEnv.IsLocked() {
|
||||
return commands.HandleVErrAndExitCode(errhand.VerboseErrorFromError(env.ErrActiveServerLock.New(dEnv.LockFile())), help)
|
||||
}
|
||||
|
||||
// TODO: Error if dEnv.DoltDB.AccessMode() == ReadOnly?
|
||||
|
||||
_, sqlCtx, closer, err := cliCtx.QueryEngine(ctx)
|
||||
if err != nil {
|
||||
|
||||
@@ -21,7 +21,6 @@ import (
|
||||
|
||||
"github.com/dolthub/dolt/go/cmd/dolt/cli"
|
||||
"github.com/dolthub/dolt/go/cmd/dolt/commands"
|
||||
"github.com/dolthub/dolt/go/cmd/dolt/errhand"
|
||||
eventsapi "github.com/dolthub/dolt/go/gen/proto/dolt/services/eventsapi/v1alpha1"
|
||||
"github.com/dolthub/dolt/go/libraries/doltcore/diff"
|
||||
"github.com/dolthub/dolt/go/libraries/doltcore/doltdb"
|
||||
@@ -100,9 +99,7 @@ func (cmd StashCmd) Exec(ctx context.Context, commandStr string, args []string,
|
||||
return 1
|
||||
}
|
||||
|
||||
if dEnv.IsLocked() {
|
||||
return commands.HandleVErrAndExitCode(errhand.VerboseErrorFromError(env.ErrActiveServerLock.New(dEnv.LockFile())), help)
|
||||
}
|
||||
// TODO: Error if dEnv.DoltDB.AccessMode() == ReadOnly?
|
||||
|
||||
err := stashChanges(ctx, dEnv, apr)
|
||||
if err != nil {
|
||||
|
||||
@@ -440,7 +440,9 @@ func runMain() int {
|
||||
var fs filesys.Filesys
|
||||
fs = filesys.LocalFS
|
||||
dEnv := env.Load(ctx, env.GetCurrentUserHomeDir, fs, doltdb.LocalDirDoltDB, Version)
|
||||
dEnv.IgnoreLockFile = ignoreLockFile
|
||||
|
||||
// TODO: Decide what we're doing with --ignore-lock-file; does it mean always operate locally?
|
||||
_ = ignoreLockFile
|
||||
|
||||
root, err := env.GetCurrentUserHomeDir()
|
||||
if err != nil {
|
||||
@@ -550,7 +552,7 @@ func runMain() int {
|
||||
}
|
||||
dEnv.FS = dataDirFS
|
||||
|
||||
mrEnv, err := env.MultiEnvForDirectory(ctx, dEnv.Config.WriteableConfig(), dataDirFS, dEnv.Version, dEnv.IgnoreLockFile, dEnv)
|
||||
mrEnv, err := env.MultiEnvForDirectory(ctx, dEnv.Config.WriteableConfig(), dataDirFS, dEnv.Version, dEnv)
|
||||
if err != nil {
|
||||
cli.PrintErrln("failed to load database names")
|
||||
return 1
|
||||
@@ -699,7 +701,11 @@ If you're interested in running this command against a remote host, hit us up on
|
||||
targetEnv = rootEnv
|
||||
}
|
||||
|
||||
isLocked, lock, err := targetEnv.GetLock()
|
||||
// TODO: if targetEnv.DoltDB.AccessMode() == ReadOnly...
|
||||
// Need to go looking for local creds to use...
|
||||
var isLocked bool
|
||||
var lock *sqlserver.LocalCreds
|
||||
var err error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
165
go/libraries/doltcore/env/environment.go
vendored
165
go/libraries/doltcore/env/environment.go
vendored
@@ -20,12 +20,9 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
ps "github.com/mitchellh/go-ps"
|
||||
goerrors "gopkg.in/src-d/go-errors.v1"
|
||||
|
||||
"github.com/dolthub/dolt/go/cmd/dolt/errhand"
|
||||
@@ -97,7 +94,6 @@ type DoltEnv struct {
|
||||
urlStr string
|
||||
hdp HomeDirProvider
|
||||
|
||||
IgnoreLockFile bool
|
||||
UserPassConfig *creds.DoltCredsForPass
|
||||
}
|
||||
|
||||
@@ -1187,164 +1183,3 @@ func (dEnv *DoltEnv) BulkDbEaFactory() editor.DbEaFactory {
|
||||
}
|
||||
return editor.NewBulkImportTEAFactory(dEnv.DoltDB.ValueReadWriter(), tmpDir)
|
||||
}
|
||||
|
||||
func (dEnv *DoltEnv) LockFile() string {
|
||||
f, _ := dEnv.FS.Abs(filepath.Join(dbfactory.DoltDir, ServerLockFile))
|
||||
return f
|
||||
}
|
||||
|
||||
// IsLocked returns true if this database's lockfile exists and the pid contained in lockfile is alive.
|
||||
func (dEnv *DoltEnv) IsLocked() bool {
|
||||
if dEnv.IgnoreLockFile {
|
||||
return false
|
||||
}
|
||||
|
||||
ans, _, _ := fsIsLocked(dEnv.FS)
|
||||
return ans
|
||||
}
|
||||
|
||||
// GetLock returns the lockfile for this database or nil if the database is not locked
|
||||
func (dEnv *DoltEnv) GetLock() (bool, *DBLock, error) {
|
||||
if dEnv.IgnoreLockFile {
|
||||
return false, nil, nil
|
||||
}
|
||||
|
||||
return fsIsLocked(dEnv.FS)
|
||||
}
|
||||
|
||||
// DBLock is a struct that contains the pid of the process that created the lockfile and the port that the server is running on
|
||||
// The secret is used by dolt to ensure that the contents of the lockfile are required to perform a local server connection
|
||||
type DBLock struct {
|
||||
Pid int
|
||||
Port int
|
||||
Secret string
|
||||
}
|
||||
|
||||
// DBLock constructor
|
||||
func NewDBLock(port int) DBLock {
|
||||
return DBLock{Pid: os.Getpid(), Port: port, Secret: uuid.New().String()}
|
||||
}
|
||||
|
||||
// Lock writes this database's lockfile with the pid of the calling process or errors if it already exists
|
||||
func (dEnv *DoltEnv) Lock(lock *DBLock) error {
|
||||
if dEnv.IgnoreLockFile {
|
||||
return nil
|
||||
}
|
||||
|
||||
if dEnv.IsLocked() {
|
||||
return ErrActiveServerLock.New(dEnv.LockFile())
|
||||
}
|
||||
|
||||
return WriteLockfile(dEnv.FS, lock)
|
||||
}
|
||||
|
||||
func LoadDBLockFile(fs filesys.Filesys, lockFilePath string) (lock *DBLock, err error) {
|
||||
rd, err := fs.OpenForRead(lockFilePath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rd.Close()
|
||||
|
||||
b := make([]byte, 256)
|
||||
n, err := rd.Read(b)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
data := strings.TrimSpace(string(b[:n]))
|
||||
|
||||
parts := strings.Split(data, ":")
|
||||
if len(parts) == 1 {
|
||||
// Legacy Lock file. We can remove this code path in a couple of months (6/2023)
|
||||
pid, err := strconv.Atoi(parts[0])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &DBLock{Pid: pid, Port: -1, Secret: ""}, nil
|
||||
}
|
||||
if len(parts) != 3 {
|
||||
return nil, fmt.Errorf("invalid lock file format")
|
||||
}
|
||||
|
||||
pid, err := strconv.Atoi(parts[0])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
port := -1
|
||||
if parts[1] != "-" {
|
||||
port, err = strconv.Atoi(parts[1])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
secret := parts[2]
|
||||
return &DBLock{Pid: pid, Port: port, Secret: secret}, nil
|
||||
}
|
||||
|
||||
// Unlock deletes this database's lockfile
|
||||
func (dEnv *DoltEnv) Unlock() error {
|
||||
if dEnv.IgnoreLockFile {
|
||||
return nil
|
||||
}
|
||||
|
||||
return dEnv.FS.DeleteFile(dEnv.LockFile())
|
||||
}
|
||||
|
||||
// WriteLockfile writes a lockfile encoding the pid of the calling process.
|
||||
func WriteLockfile(fs filesys.Filesys, lock *DBLock) error {
|
||||
// if the DoltDir doesn't exist, create it.
|
||||
doltDir, _ := fs.Abs(dbfactory.DoltDir)
|
||||
err := fs.MkDirs(doltDir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
lockFile, _ := fs.Abs(filepath.Join(dbfactory.DoltDir, ServerLockFile))
|
||||
|
||||
portStr := strconv.Itoa(lock.Port)
|
||||
if lock.Port < 0 {
|
||||
portStr = "-"
|
||||
}
|
||||
|
||||
err = fs.WriteFile(lockFile, []byte(fmt.Sprintf("%d:%s:%s", lock.Pid, portStr, lock.Secret)), 0600)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// fsIsLocked returns true if the database in qeustion is locked. Two additional return values are lock and err. In the
|
||||
// event that the DB is locked (locked == true), then either the lock or an error is returned. In either case,
|
||||
// the caller should not attempt to use the DB. If lock is returned, in some cases you can use it to connect to the
|
||||
// server that has locked the DB. If an error is returned, then no further processing should be done.
|
||||
func fsIsLocked(fs filesys.Filesys) (locked bool, lock *DBLock, err error) {
|
||||
lockFile, _ := fs.Abs(filepath.Join(dbfactory.DoltDir, ServerLockFile))
|
||||
|
||||
ok, _ := fs.Exists(lockFile)
|
||||
if !ok {
|
||||
return false, nil, nil
|
||||
}
|
||||
|
||||
loadedLock, err := LoadDBLockFile(fs, lockFile)
|
||||
if err != nil { // if there's any error assume that env is locked since the file exists
|
||||
return true, nil, err
|
||||
}
|
||||
|
||||
// If the PID is for this process, then ignore the lock file. This happens frequently with docker containers
|
||||
// https://github.com/dolthub/dolt/issues/6183.
|
||||
if os.Getpid() == loadedLock.Pid {
|
||||
return false, nil, nil
|
||||
}
|
||||
|
||||
// Check whether the pid that spawned the lock file is still running. Ignore it if not.
|
||||
proc, err := ps.FindProcess(loadedLock.Pid)
|
||||
if err != nil {
|
||||
return true, nil, err // This will happen if we can't load the OS processes. Assume locked.
|
||||
}
|
||||
if proc != nil {
|
||||
return true, loadedLock, nil // The process is still running. Return the lock details so the caller can connect to it if appropriate.
|
||||
}
|
||||
|
||||
return false, nil, nil
|
||||
}
|
||||
|
||||
5
go/libraries/doltcore/env/multi_repo_env.go
vendored
5
go/libraries/doltcore/env/multi_repo_env.go
vendored
@@ -53,12 +53,11 @@ type MultiRepoEnv struct {
|
||||
fs filesys.Filesys
|
||||
cfg config.ReadWriteConfig
|
||||
dialProvider dbfactory.GRPCDialProvider
|
||||
ignoreLockFile bool
|
||||
}
|
||||
|
||||
// NewMultiEnv returns a new MultiRepoEnv instance dirived from a root DoltEnv instance.
|
||||
func MultiEnvForSingleEnv(ctx context.Context, env *DoltEnv) (*MultiRepoEnv, error) {
|
||||
return MultiEnvForDirectory(ctx, env.Config.WriteableConfig(), env.FS, env.Version, env.IgnoreLockFile, env)
|
||||
return MultiEnvForDirectory(ctx, env.Config.WriteableConfig(), env.FS, env.Version, env)
|
||||
}
|
||||
|
||||
// MultiEnvForDirectory returns a MultiRepoEnv for the directory rooted at the file system given. The doltEnv from the
|
||||
@@ -69,7 +68,6 @@ func MultiEnvForDirectory(
|
||||
config config.ReadWriteConfig,
|
||||
dataDirFS filesys.Filesys,
|
||||
version string,
|
||||
ignoreLockFile bool,
|
||||
dEnv *DoltEnv,
|
||||
) (*MultiRepoEnv, error) {
|
||||
// Load current dataDirFS and put into mr env
|
||||
@@ -94,7 +92,6 @@ func MultiEnvForDirectory(
|
||||
fs: dataDirFS,
|
||||
cfg: config,
|
||||
dialProvider: NewGRPCDialProviderFromDoltEnv(newDEnv),
|
||||
ignoreLockFile: ignoreLockFile,
|
||||
}
|
||||
|
||||
envSet := map[string]*DoltEnv{}
|
||||
|
||||
@@ -121,7 +121,7 @@ func TestMultiEnvForDirectory(t *testing.T) {
|
||||
envPath := filepath.Join(rootPath, " test---name _ 123")
|
||||
dEnv := initRepoWithRelativePath(t, envPath, hdp)
|
||||
|
||||
mrEnv, err := MultiEnvForDirectory(context.Background(), dEnv.Config.WriteableConfig(), dEnv.FS, dEnv.Version, dEnv.IgnoreLockFile, dEnv)
|
||||
mrEnv, err := MultiEnvForDirectory(context.Background(), dEnv.Config.WriteableConfig(), dEnv.FS, dEnv.Version, dEnv)
|
||||
require.NoError(t, err)
|
||||
assert.Len(t, mrEnv.envs, 1)
|
||||
|
||||
@@ -158,7 +158,7 @@ func TestMultiEnvForDirectoryWithMultipleRepos(t *testing.T) {
|
||||
subEnv1 := initRepoWithRelativePath(t, filepath.Join(envPath, "abc"), hdp)
|
||||
subEnv2 := initRepoWithRelativePath(t, filepath.Join(envPath, "def"), hdp)
|
||||
|
||||
mrEnv, err := MultiEnvForDirectory(context.Background(), dEnv.Config.WriteableConfig(), dEnv.FS, dEnv.Version, dEnv.IgnoreLockFile, dEnv)
|
||||
mrEnv, err := MultiEnvForDirectory(context.Background(), dEnv.Config.WriteableConfig(), dEnv.FS, dEnv.Version, dEnv)
|
||||
require.NoError(t, err)
|
||||
assert.Len(t, mrEnv.envs, 3)
|
||||
|
||||
|
||||
@@ -363,7 +363,7 @@ func setupConcurrencyTest(t *testing.T, ctx context.Context) (dEnv *env.DoltEnv)
|
||||
}
|
||||
|
||||
func engineFromEnvironment(ctx context.Context, dEnv *env.DoltEnv) (dbName string, eng *engine.SqlEngine) {
|
||||
mrEnv, err := env.MultiEnvForDirectory(ctx, dEnv.Config.WriteableConfig(), dEnv.FS, dEnv.Version, dEnv.IgnoreLockFile, dEnv)
|
||||
mrEnv, err := env.MultiEnvForDirectory(ctx, dEnv.Config.WriteableConfig(), dEnv.FS, dEnv.Version, dEnv)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ type sqlEngineTableReader struct {
|
||||
}
|
||||
|
||||
func NewSqlEngineReader(ctx context.Context, dEnv *env.DoltEnv, tableName string) (*sqlEngineTableReader, error) {
|
||||
mrEnv, err := env.MultiEnvForDirectory(ctx, dEnv.Config.WriteableConfig(), dEnv.FS, dEnv.Version, dEnv.IgnoreLockFile, dEnv)
|
||||
mrEnv, err := env.MultiEnvForDirectory(ctx, dEnv.Config.WriteableConfig(), dEnv.FS, dEnv.Version, dEnv)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -62,11 +62,9 @@ type SqlEngineTableWriter struct {
|
||||
}
|
||||
|
||||
func NewSqlEngineTableWriter(ctx context.Context, dEnv *env.DoltEnv, createTableSchema, rowOperationSchema schema.Schema, options *MoverOptions, statsCB noms.StatsCB) (*SqlEngineTableWriter, error) {
|
||||
if dEnv.IsLocked() {
|
||||
return nil, env.ErrActiveServerLock.New(dEnv.LockFile())
|
||||
}
|
||||
// TODO: Assert that dEnv.DoltDB.AccessMode() != ReadOnly?
|
||||
|
||||
mrEnv, err := env.MultiEnvForDirectory(ctx, dEnv.Config.WriteableConfig(), dEnv.FS, dEnv.Version, dEnv.IgnoreLockFile, dEnv)
|
||||
mrEnv, err := env.MultiEnvForDirectory(ctx, dEnv.Config.WriteableConfig(), dEnv.FS, dEnv.Version, dEnv)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -420,7 +420,7 @@ func (d *DoltHarness) newProvider() sql.MutableDatabaseProvider {
|
||||
store := dEnv.DoltDB.ValueReadWriter().(*types.ValueStore)
|
||||
store.SetValidateContentAddresses(true)
|
||||
|
||||
mrEnv, err := env.MultiEnvForDirectory(context.Background(), dEnv.Config.WriteableConfig(), dEnv.FS, dEnv.Version, dEnv.IgnoreLockFile, dEnv)
|
||||
mrEnv, err := env.MultiEnvForDirectory(context.Background(), dEnv.Config.WriteableConfig(), dEnv.FS, dEnv.Version, dEnv)
|
||||
require.NoError(d.t, err)
|
||||
d.multiRepoEnv = mrEnv
|
||||
|
||||
|
||||
@@ -95,7 +95,7 @@ func setupIndexes(t *testing.T, tableName, insertQuery string) (*sqle.Engine, *s
|
||||
cols: idxv2v1Cols,
|
||||
}
|
||||
|
||||
mrEnv, err := env.MultiEnvForDirectory(context.Background(), dEnv.Config.WriteableConfig(), dEnv.FS, dEnv.Version, dEnv.IgnoreLockFile, dEnv)
|
||||
mrEnv, err := env.MultiEnvForDirectory(context.Background(), dEnv.Config.WriteableConfig(), dEnv.FS, dEnv.Version, dEnv)
|
||||
require.NoError(t, err)
|
||||
b := env.GetDefaultInitBranch(dEnv.Config)
|
||||
pro, err := dsqle.NewDoltDatabaseProviderWithDatabase(b, mrEnv.FileSystem(), db, dEnv.FS)
|
||||
|
||||
@@ -286,7 +286,7 @@ func sqlNewEngine(dEnv *env.DoltEnv) (*sqle.Engine, dsess.DoltDatabaseProvider,
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
mrEnv, err := env.MultiEnvForDirectory(context.Background(), dEnv.Config.WriteableConfig(), dEnv.FS, dEnv.Version, dEnv.IgnoreLockFile, dEnv)
|
||||
mrEnv, err := env.MultiEnvForDirectory(context.Background(), dEnv.Config.WriteableConfig(), dEnv.FS, dEnv.Version, dEnv)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
@@ -1108,7 +1108,7 @@ func newTestEngine(ctx context.Context, dEnv *env.DoltEnv) (*gms.Engine, *sql.Co
|
||||
panic(err)
|
||||
}
|
||||
|
||||
mrEnv, err := env.MultiEnvForDirectory(ctx, dEnv.Config.WriteableConfig(), dEnv.FS, dEnv.Version, dEnv.IgnoreLockFile, dEnv)
|
||||
mrEnv, err := env.MultiEnvForDirectory(ctx, dEnv.Config.WriteableConfig(), dEnv.FS, dEnv.Version, dEnv)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
@@ -115,7 +115,7 @@ func setupBenchmark(t *testing.B, dEnv *env.DoltEnv) (*sql.Context, *engine.SqlE
|
||||
Autocommit: true,
|
||||
}
|
||||
|
||||
mrEnv, err := env.MultiEnvForDirectory(ctx, dEnv.Config.WriteableConfig(), dEnv.FS, dEnv.Version, dEnv.IgnoreLockFile, dEnv)
|
||||
mrEnv, err := env.MultiEnvForDirectory(ctx, dEnv.Config.WriteableConfig(), dEnv.FS, dEnv.Version, dEnv)
|
||||
require.NoError(t, err)
|
||||
|
||||
eng, err := engine.NewSqlEngine(ctx, mrEnv, config)
|
||||
|
||||
Reference in New Issue
Block a user