// Copyright 2020 PingCAP, 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, // See the License for the specific language governing permissions and // limitations under the License. package disk import ( "io/ioutil" "os" "path/filepath" "github.com/danjacques/gofslock/fslock" "github.com/pingcap/errors" "github.com/pingcap/log" "github.com/pingcap/parser/terror" "github.com/pingcap/tidb/config" "go.uber.org/zap" "golang.org/x/sync/singleflight" ) var ( tempDirLock fslock.Handle sf singleflight.Group ) const ( lockFile = "_dir.lock" recordDir = "record" ) // CheckAndInitTempDir check whether the temp directory is existed. // If not, initializes the temp directory. func CheckAndInitTempDir() (err error) { _, err, _ = sf.Do("tempDir", func() (value interface{}, err error) { if !checkTempDirExist() { log.Info("Tmp-storage-path not found. Try to initialize TempDir.") err = InitializeTempDir() } return }) return } func checkTempDirExist() bool { tempDir := config.GetGlobalConfig().TempStoragePath _, err := os.Stat(tempDir) if err != nil && !os.IsExist(err) { return false } return true } // InitializeTempDir initializes the temp directory. func InitializeTempDir() error { tempDir := config.GetGlobalConfig().TempStoragePath _, err := os.Stat(tempDir) if err != nil && !os.IsExist(err) { err = os.MkdirAll(tempDir, 0755) if err != nil { return err } } tempDirLock, err = fslock.Lock(filepath.Join(tempDir, lockFile)) if err != nil { switch err { case fslock.ErrLockHeld: log.Error("The current temporary storage dir has been occupied by another instance, "+ "check tmp-storage-path config and make sure they are different.", zap.String("TempStoragePath", tempDir), zap.Error(err)) default: log.Error("Failed to acquire exclusive lock on the temporary storage dir.", zap.String("TempStoragePath", tempDir), zap.Error(err)) } return err } subDirs, err := ioutil.ReadDir(tempDir) if err != nil { return err } // If it exists others files except lock file, creates another goroutine to clean them. if len(subDirs) > 2 { go func() { for _, subDir := range subDirs { // Do not remove the lock file. switch subDir.Name() { case lockFile, recordDir: continue } err := os.RemoveAll(filepath.Join(tempDir, subDir.Name())) if err != nil { log.Warn("Remove temporary file error", zap.String("tempStorageSubDir", filepath.Join(tempDir, subDir.Name())), zap.Error(err)) } } }() } return nil } // CleanUp releases the directory lock when exiting TiDB. func CleanUp() { if tempDirLock != nil { err := tempDirLock.Unlock() terror.Log(errors.Trace(err)) } } // CheckAndCreateDir check whether the directory is existed. If not, then create it. func CheckAndCreateDir(path string) error { _, err := os.Stat(path) if err != nil && !os.IsExist(err) { err = os.MkdirAll(path, 0755) if err != nil { return err } } return nil }