2022-07-05 13:47:39 +00:00
|
|
|
package blobovniczatree
|
|
|
|
|
|
|
|
import (
|
2023-05-28 19:37:37 +00:00
|
|
|
"context"
|
2023-09-19 15:08:38 +00:00
|
|
|
"errors"
|
|
|
|
"os"
|
|
|
|
"path/filepath"
|
|
|
|
"strings"
|
2022-07-05 13:47:39 +00:00
|
|
|
|
2023-04-12 14:35:10 +00:00
|
|
|
"git.frostfs.info/TrueCloudLab/frostfs-node/internal/logs"
|
2023-09-20 14:46:10 +00:00
|
|
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/util"
|
2022-07-05 13:47:39 +00:00
|
|
|
"go.uber.org/zap"
|
|
|
|
)
|
|
|
|
|
2023-09-19 15:08:38 +00:00
|
|
|
var errFailedToChangeExtensionReadOnly = errors.New("failed to change blobovnicza extension: read only mode")
|
|
|
|
|
2022-07-05 13:47:39 +00:00
|
|
|
// Open opens blobovnicza tree.
|
|
|
|
func (b *Blobovniczas) Open(readOnly bool) error {
|
|
|
|
b.readOnly = readOnly
|
2023-06-05 07:25:25 +00:00
|
|
|
b.metrics.SetMode(readOnly)
|
2023-08-30 20:36:48 +00:00
|
|
|
b.openManagers()
|
2022-07-05 13:47:39 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Init initializes blobovnicza tree.
|
|
|
|
//
|
|
|
|
// Should be called exactly once.
|
|
|
|
func (b *Blobovniczas) Init() error {
|
2023-04-12 14:35:10 +00:00
|
|
|
b.log.Debug(logs.BlobovniczatreeInitializingBlobovniczas)
|
2022-07-05 13:47:39 +00:00
|
|
|
|
2023-09-19 15:08:38 +00:00
|
|
|
b.log.Debug(logs.BlobovniczaTreeFixingFileExtensions)
|
|
|
|
if err := b.addDBExtensionToDBs(b.rootPath, 0); err != nil {
|
|
|
|
b.log.Error(logs.BlobovniczaTreeFixingFileExtensionsFailed, zap.Error(err))
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
b.log.Debug(logs.BlobovniczaTreeFixingFileExtensionsCompletedSuccessfully)
|
|
|
|
|
2022-07-05 13:47:39 +00:00
|
|
|
if b.readOnly {
|
2023-04-12 14:35:10 +00:00
|
|
|
b.log.Debug(logs.BlobovniczatreeReadonlyModeSkipBlobovniczasInitialization)
|
2022-07-05 13:47:39 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2023-09-20 14:46:10 +00:00
|
|
|
return b.initializeDBs(context.TODO())
|
|
|
|
}
|
|
|
|
|
|
|
|
func (b *Blobovniczas) initializeDBs(ctx context.Context) error {
|
|
|
|
err := util.MkdirAllX(b.rootPath, b.perm)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
visited := make(map[string]struct{})
|
|
|
|
err = b.iterateExistingDBPaths(ctx, func(p string) (bool, error) {
|
|
|
|
visited[p] = struct{}{}
|
|
|
|
shBlz := b.getBlobovniczaWithoutCaching(p)
|
|
|
|
_, err := shBlz.Open()
|
|
|
|
if err != nil {
|
|
|
|
return true, err
|
|
|
|
}
|
|
|
|
defer shBlz.Close()
|
|
|
|
|
|
|
|
b.log.Debug(logs.BlobovniczatreeBlobovniczaSuccessfullyInitializedClosing, zap.String("id", p))
|
|
|
|
return false, nil
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
return b.iterateSortedLeaves(ctx, nil, func(p string) (bool, error) {
|
|
|
|
if _, found := visited[p]; found {
|
|
|
|
return false, nil
|
|
|
|
}
|
2023-08-31 08:32:09 +00:00
|
|
|
shBlz := b.getBlobovniczaWithoutCaching(p)
|
2023-08-30 20:36:48 +00:00
|
|
|
_, err := shBlz.Open()
|
2022-08-29 07:49:14 +00:00
|
|
|
if err != nil {
|
|
|
|
return true, err
|
|
|
|
}
|
2023-08-30 20:36:48 +00:00
|
|
|
defer shBlz.Close()
|
2022-07-05 13:47:39 +00:00
|
|
|
|
2023-04-12 14:35:10 +00:00
|
|
|
b.log.Debug(logs.BlobovniczatreeBlobovniczaSuccessfullyInitializedClosing, zap.String("id", p))
|
2022-08-29 07:49:14 +00:00
|
|
|
return false, nil
|
2022-07-05 13:47:39 +00:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2023-08-30 20:36:48 +00:00
|
|
|
func (b *Blobovniczas) openManagers() {
|
2023-10-31 11:56:55 +00:00
|
|
|
b.commondbManager.Open() // order important
|
2023-08-30 20:36:48 +00:00
|
|
|
b.activeDBManager.Open()
|
2023-08-31 08:32:09 +00:00
|
|
|
b.dbCache.Open()
|
2023-08-30 20:36:48 +00:00
|
|
|
}
|
|
|
|
|
2022-07-08 07:09:48 +00:00
|
|
|
// Close implements common.Storage.
|
2022-07-05 13:47:39 +00:00
|
|
|
func (b *Blobovniczas) Close() error {
|
2023-10-31 11:56:55 +00:00
|
|
|
b.dbCache.Close() // order important
|
2023-08-31 08:32:09 +00:00
|
|
|
b.activeDBManager.Close()
|
2023-08-30 20:36:48 +00:00
|
|
|
b.commondbManager.Close()
|
2022-07-05 13:47:39 +00:00
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
2022-07-18 06:16:36 +00:00
|
|
|
|
2023-08-31 08:32:09 +00:00
|
|
|
// returns blobovnicza with path p
|
2022-07-18 06:16:36 +00:00
|
|
|
//
|
2023-08-31 08:32:09 +00:00
|
|
|
// If blobovnicza is already cached, instance from cache is returned w/o changes.
|
|
|
|
func (b *Blobovniczas) getBlobovnicza(p string) *sharedDB {
|
|
|
|
return b.dbCache.GetOrCreate(p)
|
2022-08-29 07:49:14 +00:00
|
|
|
}
|
|
|
|
|
2023-08-31 08:32:09 +00:00
|
|
|
func (b *Blobovniczas) getBlobovniczaWithoutCaching(p string) *sharedDB {
|
2023-08-30 20:36:48 +00:00
|
|
|
return b.commondbManager.GetByPath(p)
|
2022-07-18 06:16:36 +00:00
|
|
|
}
|
2023-09-19 15:08:38 +00:00
|
|
|
|
|
|
|
func (b *Blobovniczas) addDBExtensionToDBs(path string, depth uint64) error {
|
|
|
|
entries, err := os.ReadDir(path)
|
|
|
|
if os.IsNotExist(err) && depth == 0 {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, entry := range entries {
|
|
|
|
if entry.IsDir() {
|
|
|
|
if err := b.addDBExtensionToDBs(filepath.Join(path, entry.Name()), depth+1); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
if strings.HasSuffix(entry.Name(), dbExtension) {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if b.readOnly {
|
|
|
|
return errFailedToChangeExtensionReadOnly
|
|
|
|
}
|
|
|
|
|
|
|
|
sourcePath := filepath.Join(path, entry.Name())
|
|
|
|
targetPath := filepath.Join(path, entry.Name()+dbExtension)
|
|
|
|
b.log.Debug(logs.BlobovniczaTreeFixingFileExtensionForFile, zap.String("source", sourcePath), zap.String("target", targetPath))
|
|
|
|
if err := os.Rename(sourcePath, targetPath); err != nil {
|
|
|
|
b.log.Error(logs.BlobovniczaTreeFixingFileExtensionFailed, zap.String("source", sourcePath), zap.String("target", targetPath), zap.Error(err))
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
b.log.Debug(logs.BlobovniczaTreeFixingFileExtensionCompletedSuccessfully, zap.String("source", sourcePath), zap.String("target", targetPath))
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|