From b7226f4c8259172d14fd677b12f5eb5144369aef Mon Sep 17 00:00:00 2001 From: Harshavardhana Date: Wed, 28 Nov 2018 17:38:23 -0800 Subject: [PATCH] Add transaction lock when migrating configs (#6878) When migrating configs it happens often that some servers fail to start due to version mismatch etc. Hold a transaction lock such that all servers get serialized. --- cmd/config-migrate.go | 56 +++++++++++++++++++++++++++++++++++++++++++ cmd/config.go | 24 ------------------- 2 files changed, 56 insertions(+), 24 deletions(-) diff --git a/cmd/config-migrate.go b/cmd/config-migrate.go index e4d00c899..6f84f29cd 100644 --- a/cmd/config-migrate.go +++ b/cmd/config-migrate.go @@ -2413,8 +2413,64 @@ func migrateV27ToV28() error { return nil } +// Migrates ${HOME}/.minio/config.json to '/.minio.sys/config/config.json' +func migrateConfigToMinioSys(objAPI ObjectLayer) (err error) { + defer func() { + // Rename config.json to config.json.deprecated only upon + // success of this function. + if err == nil { + os.Rename(getConfigFile(), getConfigFile()+".deprecated") + } + }() + + configFile := path.Join(minioConfigPrefix, minioConfigFile) + // Construct path to config.json for the given bucket. + transactionConfigFile := configFile + ".transaction" + + // As object layer's GetObject() and PutObject() take respective lock on minioMetaBucket + // and configFile, take a transaction lock to avoid data race between readConfig() + // and saveConfig(). + objLock := globalNSMutex.NewNSLock(minioMetaBucket, transactionConfigFile) + if err = objLock.GetLock(globalOperationTimeout); err != nil { + return err + } + defer objLock.Unlock() + + // Verify if backend already has the file. + if err = checkConfig(context.Background(), objAPI, configFile); err != errConfigNotFound { + return err + } // if errConfigNotFound proceed to migrate.. + + var config = &serverConfig{} + if _, err = Load(getConfigFile(), config); err != nil { + if !os.IsNotExist(err) { + return err + } + // Read from deprecate file as well if necessary. + if _, err = Load(getConfigFile()+".deprecated", config); err != nil { + return err + } + } + + return saveServerConfig(context.Background(), objAPI, config) +} + // Migrates '.minio.sys/config.json' to v32. func migrateMinioSysConfig(objAPI ObjectLayer) error { + configFile := path.Join(minioConfigPrefix, minioConfigFile) + + // Construct path to config.json for the given bucket. + transactionConfigFile := configFile + ".transaction" + + // As object layer's GetObject() and PutObject() take respective lock on minioMetaBucket + // and configFile, take a transaction lock to avoid data race between readConfig() + // and saveConfig(). + objLock := globalNSMutex.NewNSLock(minioMetaBucket, transactionConfigFile) + if err := objLock.GetLock(globalOperationTimeout); err != nil { + return err + } + defer objLock.Unlock() + if err := migrateV27ToV28MinioSys(objAPI); err != nil { return err } diff --git a/cmd/config.go b/cmd/config.go index 0319a3933..8a7d1d7e4 100644 --- a/cmd/config.go +++ b/cmd/config.go @@ -156,30 +156,6 @@ func NewConfigSys() *ConfigSys { return &ConfigSys{} } -// Migrates ${HOME}/.minio/config.json to '/.minio.sys/config/config.json' -func migrateConfigToMinioSys(objAPI ObjectLayer) error { - defer os.Rename(getConfigFile(), getConfigFile()+".deprecated") - - configFile := path.Join(minioConfigPrefix, minioConfigFile) - // Verify if backend already has the file. - if err := checkConfig(context.Background(), objAPI, configFile); err != errConfigNotFound { - return err - } // if errConfigNotFound proceed to migrate.. - - var config = &serverConfig{} - if _, err := Load(getConfigFile(), config); err != nil { - if !os.IsNotExist(err) { - return err - } - // Read from deprecate file as well if necessary. - if _, err = Load(getConfigFile()+".deprecated", config); err != nil { - return err - } - } - - return saveServerConfig(context.Background(), objAPI, config) -} - // Initialize and load config from remote etcd or local config directory func initConfig(objAPI ObjectLayer) error { if objAPI == nil {