When DB files can't be read off disk, fail with a clear error rather than a panic

This commit is contained in:
Neil Macneale IV
2026-01-13 15:41:46 -08:00
parent c408c09500
commit 6ab64f393b
4 changed files with 60 additions and 7 deletions
+5 -1
View File
@@ -192,7 +192,11 @@ func ConfigureServices(
AssertNoDatabasesInAccessModeReadOnly := &svcs.AnonService{
InitF: func(ctx context.Context) (err error) {
return mrEnv.Iter(func(name string, dEnv *env.DoltEnv) (stop bool, err error) {
if dEnv.IsAccessModeReadOnly(ctx) {
readOnly, err := dEnv.IsAccessModeReadOnly(ctx)
if err != nil {
return true, err
}
if readOnly {
return true, ErrCouldNotLockDatabase.New(name)
}
return false, nil
+21 -4
View File
@@ -708,7 +708,15 @@ If you're interested in running this command against a remote host, hit us up on
}
var lookForServer bool
if targetEnv.DoltDB(ctx) != nil && targetEnv.IsAccessModeReadOnly(ctx) {
readOnly, err := targetEnv.IsAccessModeReadOnly(ctx)
if err != nil {
// Missing dolt data dir is expected if we are in a top level server directory. Other errors are not.
if !errors.Is(err, doltdb.ErrMissingDoltDataDir) {
return nil, err
}
}
if readOnly {
// If the loaded target environment has a doltDB and we do not
// have access to it, we look for a server.
lookForServer = true
@@ -718,12 +726,21 @@ If you're interested in running this command against a remote host, hit us up on
// repositories in our MultiEnv are ReadOnly. This includes the
// case where there are no repositories in our MultiEnv
var allReposAreReadOnly bool = true
mrEnv.Iter(func(name string, dEnv *env.DoltEnv) (stop bool, err error) {
if dEnv.DoltDB(ctx) != nil {
allReposAreReadOnly = allReposAreReadOnly && dEnv.IsAccessModeReadOnly(ctx)
err = mrEnv.Iter(func(name string, dEnv *env.DoltEnv) (stop bool, err error) {
readOnly, err := dEnv.IsAccessModeReadOnly(ctx)
if err != nil {
return true, fmt.Errorf("Failed to load database %s due to error: %w", name, err)
}
allReposAreReadOnly = allReposAreReadOnly && readOnly
return !allReposAreReadOnly, nil
})
if err != nil {
return nil, err
}
lookForServer = allReposAreReadOnly
}
if lookForServer {
+12 -2
View File
@@ -110,6 +110,8 @@ func NewDoltEnv(version string, config *DoltCliConfig, repoState *RepoState, dol
}
}
// DoltDB returns the dolt database for this environment, loading it if necessary. If loading fails,
// |dEnv.DBLoadError| will be non-nil. The caller is responsible for checking this error.
func (dEnv *DoltEnv) DoltDB(ctx context.Context) *doltdb.DoltDB {
if dEnv.doltDB == nil {
LoadDoltDB(ctx, dEnv.FS, dEnv.urlStr, dEnv)
@@ -1319,6 +1321,14 @@ func (dEnv *DoltEnv) BulkDbEaFactory(ctx context.Context) editor.DbEaFactory {
return editor.NewBulkImportTEAFactory(dEnv.DoltDB(ctx).ValueReadWriter(), tmpDir)
}
func (dEnv *DoltEnv) IsAccessModeReadOnly(ctx context.Context) bool {
return dEnv.DoltDB(ctx).AccessMode() == chunks.ExclusiveAccessMode_ReadOnly
func (dEnv *DoltEnv) IsAccessModeReadOnly(ctx context.Context) (bool, error) {
db := dEnv.DoltDB(ctx)
if db == nil {
if dEnv.DBLoadError != nil {
return false, dEnv.DBLoadError
}
return false, errors.New("DoltDB failed to initialize but no error was recorded")
}
return db.AccessMode() == chunks.ExclusiveAccessMode_ReadOnly, nil
}
+22
View File
@@ -2265,3 +2265,25 @@ EOF
[ "$status" -eq 0 ]
[[ ! "$output" =~ "Empty database name" ]] || false
}
@test "sql-server: read permission failure clearly communicated" {
start_sql_server > server_log.txt 2>&1 && sleep 0.5
stop_sql_server 1 && sleep 0.5
# Server log should say nothing about permissions.
run grep -F "permission denied" server_log.txt
[ $status -eq 1 ]
chmod 0000 repo1/.dolt/noms/manifest
start_sql_server > server_log.txt 2>&1 && sleep 0.5
grep -F "permission denied" server_log.txt
# revert, and do the same with the journal file.
chmod 0600 repo1/.dolt/noms/manifest
chmod 0000 repo1/.dolt/noms/vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
start_sql_server > server_log.txt 2>&1 && sleep 0.5
grep -F "permission denied" server_log.txt
}