forked from TrueCloudLab/frostfs-node
[#661] blobovniczatree: Use .db extension for db files
Signed-off-by: Dmitrii Stepanov <d.stepanov@yadro.com>
This commit is contained in:
parent
44552a849b
commit
484eb59893
15 changed files with 160 additions and 24 deletions
|
@ -5,7 +5,7 @@ import (
|
|||
"fmt"
|
||||
|
||||
common "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-lens/internal"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/blobovnicza"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/blobstor/blobovniczatree"
|
||||
meta "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/metabase"
|
||||
objectSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object"
|
||||
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
|
||||
|
@ -40,7 +40,7 @@ func inspectFunc(cmd *cobra.Command, _ []string) {
|
|||
common.ExitOnErr(cmd, common.Errf("could not check if the obj is small: %w", err))
|
||||
|
||||
if id := resStorageID.StorageID(); id != nil {
|
||||
cmd.Printf("Object storageID: %s\n\n", blobovnicza.NewIDFromBytes(id).String())
|
||||
cmd.Printf("Object storageID: %s\n\n", blobovniczatree.NewIDFromBytes(id).Path())
|
||||
} else {
|
||||
cmd.Printf("Object does not contain storageID\n\n")
|
||||
}
|
||||
|
|
|
@ -523,4 +523,10 @@ const (
|
|||
BlobstoreRebuildStarted = "blobstore rebuild started"
|
||||
BlobstoreRebuildCompletedSuccessfully = "blobstore rebuild completed successfully"
|
||||
BlobstoreRebuildStopped = "blobstore rebuild stopped"
|
||||
BlobovniczaTreeFixingFileExtensions = "fixing blobovnicza tree file extensions..."
|
||||
BlobovniczaTreeFixingFileExtensionsCompletedSuccessfully = "fixing blobovnicza tree file extensions completed successfully"
|
||||
BlobovniczaTreeFixingFileExtensionsFailed = "failed to fix blobovnicza tree file extensions"
|
||||
BlobovniczaTreeFixingFileExtensionForFile = "fixing blobovnicza file extension..."
|
||||
BlobovniczaTreeFixingFileExtensionCompletedSuccessfully = "fixing blobovnicza file extension completed successfully"
|
||||
BlobovniczaTreeFixingFileExtensionFailed = "failed to fix blobovnicza file extension"
|
||||
)
|
||||
|
|
|
@ -154,7 +154,7 @@ func (m *activeDBManager) getNextSharedDB(lvlPath string) (*sharedDB, error) {
|
|||
var next *sharedDB
|
||||
|
||||
for iterCount < m.leafWidth {
|
||||
path := filepath.Join(lvlPath, u64ToHexString(idx))
|
||||
path := filepath.Join(lvlPath, u64ToHexStringExt(idx))
|
||||
shDB := m.dbManager.GetByPath(path)
|
||||
db, err := shDB.Open() // open db to hold active DB open, will be closed if db is full, after m.replace or by activeDBManager.Close()
|
||||
if err != nil {
|
||||
|
|
|
@ -4,6 +4,7 @@ import (
|
|||
"errors"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/blobstor/common"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/blobstor/compression"
|
||||
|
@ -63,6 +64,10 @@ var _ common.Storage = (*Blobovniczas)(nil)
|
|||
|
||||
var errPutFailed = errors.New("could not save the object in any blobovnicza")
|
||||
|
||||
const (
|
||||
dbExtension = ".db"
|
||||
)
|
||||
|
||||
// NewBlobovniczaTree returns new instance of blobovniczas tree.
|
||||
func NewBlobovniczaTree(opts ...Option) (blz *Blobovniczas) {
|
||||
blz = new(Blobovniczas)
|
||||
|
@ -94,14 +99,16 @@ func addressHash(addr *oid.Address, path string) uint64 {
|
|||
return hrw.StringHash(a + path)
|
||||
}
|
||||
|
||||
// converts uint64 to hex string.
|
||||
func u64ToHexString(ind uint64) string {
|
||||
return strconv.FormatUint(ind, 16)
|
||||
}
|
||||
|
||||
// converts uint64 hex string to uint64.
|
||||
func u64ToHexStringExt(ind uint64) string {
|
||||
return strconv.FormatUint(ind, 16) + dbExtension
|
||||
}
|
||||
|
||||
func u64FromHexString(str string) uint64 {
|
||||
v, err := strconv.ParseUint(str, 16, 64)
|
||||
v, err := strconv.ParseUint(strings.TrimSuffix(str, dbExtension), 16, 64)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("blobovnicza name is not an index %s", str))
|
||||
}
|
||||
|
|
|
@ -2,11 +2,17 @@ package blobovniczatree
|
|||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-node/internal/logs"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
var errFailedToChangeExtensionReadOnly = errors.New("failed to change blobovnicza extension: read only mode")
|
||||
|
||||
// Open opens blobovnicza tree.
|
||||
func (b *Blobovniczas) Open(readOnly bool) error {
|
||||
b.readOnly = readOnly
|
||||
|
@ -21,6 +27,13 @@ func (b *Blobovniczas) Open(readOnly bool) error {
|
|||
func (b *Blobovniczas) Init() error {
|
||||
b.log.Debug(logs.BlobovniczatreeInitializingBlobovniczas)
|
||||
|
||||
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)
|
||||
|
||||
if b.readOnly {
|
||||
b.log.Debug(logs.BlobovniczatreeReadonlyModeSkipBlobovniczasInitialization)
|
||||
return nil
|
||||
|
@ -64,3 +77,37 @@ func (b *Blobovniczas) getBlobovnicza(p string) *sharedDB {
|
|||
func (b *Blobovniczas) getBlobovniczaWithoutCaching(p string) *sharedDB {
|
||||
return b.commondbManager.GetByPath(p)
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
|
|
@ -0,0 +1,69 @@
|
|||
package blobovniczatree
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/blobovnicza"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestDBExtensionFix(t *testing.T) {
|
||||
root := t.TempDir()
|
||||
createTestTree(t, 0, 2, 3, root)
|
||||
t.Run("adds suffix if not exists", func(t *testing.T) {
|
||||
openAndCloseTestTree(t, 2, 3, root)
|
||||
validateTestTree(t, root)
|
||||
})
|
||||
|
||||
t.Run("not adds second suffix if exists", func(t *testing.T) {
|
||||
openAndCloseTestTree(t, 2, 3, root)
|
||||
validateTestTree(t, root)
|
||||
})
|
||||
}
|
||||
|
||||
func createTestTree(t *testing.T, currentDepth, depth, width uint64, path string) {
|
||||
if currentDepth == depth {
|
||||
var w uint64
|
||||
for ; w < width; w++ {
|
||||
dbPath := filepath.Join(path, u64ToHexString(w))
|
||||
b := blobovnicza.New(blobovnicza.WithPath(dbPath))
|
||||
require.NoError(t, b.Open())
|
||||
require.NoError(t, b.Init())
|
||||
require.NoError(t, b.Close())
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
var w uint64
|
||||
for ; w < width; w++ {
|
||||
createTestTree(t, currentDepth+1, depth, width, filepath.Join(path, u64ToHexString(w)))
|
||||
}
|
||||
}
|
||||
|
||||
func openAndCloseTestTree(t *testing.T, depth, width uint64, path string) {
|
||||
blz := NewBlobovniczaTree(
|
||||
WithBlobovniczaShallowDepth(depth),
|
||||
WithBlobovniczaShallowWidth(width),
|
||||
WithRootPath(path),
|
||||
)
|
||||
require.NoError(t, blz.Open(false))
|
||||
require.NoError(t, blz.Init())
|
||||
require.NoError(t, blz.Close())
|
||||
}
|
||||
|
||||
func validateTestTree(t *testing.T, path string) {
|
||||
entries, err := os.ReadDir(path)
|
||||
require.NoError(t, err)
|
||||
|
||||
for _, entry := range entries {
|
||||
if entry.IsDir() {
|
||||
validateTestTree(t, filepath.Join(path, entry.Name()))
|
||||
} else {
|
||||
require.True(t, strings.HasSuffix(entry.Name(), dbExtension))
|
||||
require.False(t, strings.HasSuffix(strings.TrimSuffix(entry.Name(), dbExtension), dbExtension))
|
||||
}
|
||||
}
|
||||
}
|
|
@ -47,8 +47,8 @@ func (b *Blobovniczas) Delete(ctx context.Context, prm common.DeletePrm) (res co
|
|||
bPrm.SetAddress(prm.Address)
|
||||
|
||||
if prm.StorageID != nil {
|
||||
id := blobovnicza.NewIDFromBytes(prm.StorageID)
|
||||
shBlz := b.getBlobovnicza(id.String())
|
||||
id := NewIDFromBytes(prm.StorageID)
|
||||
shBlz := b.getBlobovnicza(id.Path())
|
||||
blz, err := shBlz.Open()
|
||||
if err != nil {
|
||||
return res, err
|
||||
|
|
|
@ -36,8 +36,8 @@ func (b *Blobovniczas) Exists(ctx context.Context, prm common.ExistsPrm) (common
|
|||
defer span.End()
|
||||
|
||||
if prm.StorageID != nil {
|
||||
id := blobovnicza.NewIDFromBytes(prm.StorageID)
|
||||
shBlz := b.getBlobovnicza(id.String())
|
||||
id := NewIDFromBytes(prm.StorageID)
|
||||
shBlz := b.getBlobovnicza(id.Path())
|
||||
blz, err := shBlz.Open()
|
||||
if err != nil {
|
||||
return common.ExistsRes{}, err
|
||||
|
|
|
@ -55,7 +55,7 @@ func TestExistsInvalidStorageID(t *testing.T) {
|
|||
|
||||
// An invalid boltdb file is created so that it returns an error when opened
|
||||
require.NoError(t, os.MkdirAll(filepath.Join(dir, relBadFileDir), os.ModePerm))
|
||||
require.NoError(t, os.WriteFile(filepath.Join(dir, relBadFileDir, badFileName), []byte("not a boltdb file content"), 0o777))
|
||||
require.NoError(t, os.WriteFile(filepath.Join(dir, relBadFileDir, badFileName+".db"), []byte("not a boltdb file content"), 0o777))
|
||||
|
||||
res, err := b.Exists(context.Background(), common.ExistsPrm{Address: addr, StorageID: []byte(filepath.Join(relBadFileDir, badFileName))})
|
||||
require.Error(t, err)
|
||||
|
|
|
@ -47,8 +47,8 @@ func (b *Blobovniczas) Get(ctx context.Context, prm common.GetPrm) (res common.G
|
|||
bPrm.SetAddress(prm.Address)
|
||||
|
||||
if prm.StorageID != nil {
|
||||
id := blobovnicza.NewIDFromBytes(prm.StorageID)
|
||||
shBlz := b.getBlobovnicza(id.String())
|
||||
id := NewIDFromBytes(prm.StorageID)
|
||||
shBlz := b.getBlobovnicza(id.Path())
|
||||
blz, err := shBlz.Open()
|
||||
if err != nil {
|
||||
return res, err
|
||||
|
|
|
@ -46,8 +46,8 @@ func (b *Blobovniczas) GetRange(ctx context.Context, prm common.GetRangePrm) (re
|
|||
defer span.End()
|
||||
|
||||
if prm.StorageID != nil {
|
||||
id := blobovnicza.NewIDFromBytes(prm.StorageID)
|
||||
shBlz := b.getBlobovnicza(id.String())
|
||||
id := NewIDFromBytes(prm.StorageID)
|
||||
shBlz := b.getBlobovnicza(id.Path())
|
||||
blz, err := shBlz.Open()
|
||||
if err != nil {
|
||||
return common.GetRangeRes{}, err
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
package blobovnicza
|
||||
package blobovniczatree
|
||||
|
||||
// ID represents Blobovnicza identifier.
|
||||
type ID []byte
|
||||
|
@ -8,8 +8,8 @@ func NewIDFromBytes(v []byte) *ID {
|
|||
return (*ID)(&v)
|
||||
}
|
||||
|
||||
func (id ID) String() string {
|
||||
return string(id)
|
||||
func (id ID) Path() string {
|
||||
return string(id) + dbExtension
|
||||
}
|
||||
|
||||
func (id ID) Bytes() []byte {
|
|
@ -4,6 +4,7 @@ import (
|
|||
"context"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-node/internal/logs"
|
||||
|
@ -54,7 +55,7 @@ func (b *Blobovniczas) Iterate(ctx context.Context, prm common.IteratePrm) (comm
|
|||
return prm.Handler(common.IterationElement{
|
||||
Address: elem.Address(),
|
||||
ObjectData: data,
|
||||
StorageID: []byte(p),
|
||||
StorageID: []byte(strings.TrimSuffix(p, dbExtension)),
|
||||
})
|
||||
}
|
||||
return nil
|
||||
|
@ -140,10 +141,16 @@ func (b *Blobovniczas) iterateSorted(ctx context.Context, addr *oid.Address, cur
|
|||
return false, ctx.Err()
|
||||
default:
|
||||
}
|
||||
|
||||
lastPart := u64ToHexString(indices[i])
|
||||
if isLeafLevel {
|
||||
lastPart = u64ToHexStringExt(indices[i])
|
||||
}
|
||||
|
||||
if i == 0 {
|
||||
curPath = append(curPath, u64ToHexString(indices[i]))
|
||||
curPath = append(curPath, lastPart)
|
||||
} else {
|
||||
curPath[len(curPath)-1] = u64ToHexString(indices[i])
|
||||
curPath[len(curPath)-1] = lastPart
|
||||
}
|
||||
|
||||
if exec {
|
||||
|
|
|
@ -117,7 +117,7 @@ func newLevelDBManager(width uint64, options []blobovnicza.Option, rootPath stri
|
|||
databases: make([]*sharedDB, width),
|
||||
}
|
||||
for idx := uint64(0); idx < width; idx++ {
|
||||
result.databases[idx] = newSharedDB(options, filepath.Join(rootPath, lvlPath, u64ToHexString(idx)), readOnly, metrics, openDBCounter, closedFlog, log)
|
||||
result.databases[idx] = newSharedDB(options, filepath.Join(rootPath, lvlPath, u64ToHexStringExt(idx)), readOnly, metrics, openDBCounter, closedFlog, log)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
|
|
@ -70,7 +70,7 @@ func (b *Blobovniczas) Put(ctx context.Context, prm common.PutPrm) (common.PutRe
|
|||
|
||||
type putIterator struct {
|
||||
B *Blobovniczas
|
||||
ID *blobovnicza.ID
|
||||
ID *ID
|
||||
AllFull bool
|
||||
PutPrm blobovnicza.PutPrm
|
||||
}
|
||||
|
@ -113,7 +113,7 @@ func (i *putIterator) iterate(ctx context.Context, lvlPath string) (bool, error)
|
|||
}
|
||||
|
||||
idx := u64FromHexString(filepath.Base(active.Path()))
|
||||
i.ID = blobovnicza.NewIDFromBytes([]byte(filepath.Join(lvlPath, u64ToHexString(idx))))
|
||||
i.ID = NewIDFromBytes([]byte(filepath.Join(lvlPath, u64ToHexString(idx))))
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue