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
4e05ce1c3a
commit
df11b40dfd
15 changed files with 160 additions and 24 deletions
|
@ -5,7 +5,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
common "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-lens/internal"
|
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"
|
meta "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/metabase"
|
||||||
objectSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object"
|
objectSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object"
|
||||||
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
|
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))
|
common.ExitOnErr(cmd, common.Errf("could not check if the obj is small: %w", err))
|
||||||
|
|
||||||
if id := resStorageID.StorageID(); id != nil {
|
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 {
|
} else {
|
||||||
cmd.Printf("Object does not contain storageID\n\n")
|
cmd.Printf("Object does not contain storageID\n\n")
|
||||||
}
|
}
|
||||||
|
|
|
@ -516,4 +516,10 @@ const (
|
||||||
BlobstoreRebuildStarted = "blobstore rebuild started"
|
BlobstoreRebuildStarted = "blobstore rebuild started"
|
||||||
BlobstoreRebuildCompletedSuccessfully = "blobstore rebuild completed successfully"
|
BlobstoreRebuildCompletedSuccessfully = "blobstore rebuild completed successfully"
|
||||||
BlobstoreRebuildStopped = "blobstore rebuild stopped"
|
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
|
var next *sharedDB
|
||||||
|
|
||||||
for iterCount < m.leafWidth {
|
for iterCount < m.leafWidth {
|
||||||
path := filepath.Join(lvlPath, u64ToHexString(idx))
|
path := filepath.Join(lvlPath, u64ToHexStringExt(idx))
|
||||||
shDB := m.dbManager.GetByPath(path)
|
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()
|
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 {
|
if err != nil {
|
||||||
|
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"strconv"
|
"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/common"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/blobstor/compression"
|
"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")
|
var errPutFailed = errors.New("could not save the object in any blobovnicza")
|
||||||
|
|
||||||
|
const (
|
||||||
|
dbExtension = ".db"
|
||||||
|
)
|
||||||
|
|
||||||
// NewBlobovniczaTree returns new instance of blobovniczas tree.
|
// NewBlobovniczaTree returns new instance of blobovniczas tree.
|
||||||
func NewBlobovniczaTree(opts ...Option) (blz *Blobovniczas) {
|
func NewBlobovniczaTree(opts ...Option) (blz *Blobovniczas) {
|
||||||
blz = new(Blobovniczas)
|
blz = new(Blobovniczas)
|
||||||
|
@ -94,14 +99,16 @@ func addressHash(addr *oid.Address, path string) uint64 {
|
||||||
return hrw.StringHash(a + path)
|
return hrw.StringHash(a + path)
|
||||||
}
|
}
|
||||||
|
|
||||||
// converts uint64 to hex string.
|
|
||||||
func u64ToHexString(ind uint64) string {
|
func u64ToHexString(ind uint64) string {
|
||||||
return strconv.FormatUint(ind, 16)
|
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 {
|
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 {
|
if err != nil {
|
||||||
panic(fmt.Sprintf("blobovnicza name is not an index %s", str))
|
panic(fmt.Sprintf("blobovnicza name is not an index %s", str))
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,11 +2,17 @@ package blobovniczatree
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"errors"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/internal/logs"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/internal/logs"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var errFailedToChangeExtensionReadOnly = errors.New("failed to change blobovnicza extension: read only mode")
|
||||||
|
|
||||||
// Open opens blobovnicza tree.
|
// Open opens blobovnicza tree.
|
||||||
func (b *Blobovniczas) Open(readOnly bool) error {
|
func (b *Blobovniczas) Open(readOnly bool) error {
|
||||||
b.readOnly = readOnly
|
b.readOnly = readOnly
|
||||||
|
@ -21,6 +27,13 @@ func (b *Blobovniczas) Open(readOnly bool) error {
|
||||||
func (b *Blobovniczas) Init() error {
|
func (b *Blobovniczas) Init() error {
|
||||||
b.log.Debug(logs.BlobovniczatreeInitializingBlobovniczas)
|
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 {
|
if b.readOnly {
|
||||||
b.log.Debug(logs.BlobovniczatreeReadonlyModeSkipBlobovniczasInitialization)
|
b.log.Debug(logs.BlobovniczatreeReadonlyModeSkipBlobovniczasInitialization)
|
||||||
return nil
|
return nil
|
||||||
|
@ -64,3 +77,37 @@ func (b *Blobovniczas) getBlobovnicza(p string) *sharedDB {
|
||||||
func (b *Blobovniczas) getBlobovniczaWithoutCaching(p string) *sharedDB {
|
func (b *Blobovniczas) getBlobovniczaWithoutCaching(p string) *sharedDB {
|
||||||
return b.commondbManager.GetByPath(p)
|
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))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -46,8 +46,8 @@ func (b *Blobovniczas) Delete(ctx context.Context, prm common.DeletePrm) (res co
|
||||||
bPrm.SetAddress(prm.Address)
|
bPrm.SetAddress(prm.Address)
|
||||||
|
|
||||||
if prm.StorageID != nil {
|
if prm.StorageID != nil {
|
||||||
id := blobovnicza.NewIDFromBytes(prm.StorageID)
|
id := NewIDFromBytes(prm.StorageID)
|
||||||
shBlz := b.getBlobovnicza(id.String())
|
shBlz := b.getBlobovnicza(id.Path())
|
||||||
blz, err := shBlz.Open()
|
blz, err := shBlz.Open()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return res, err
|
return res, err
|
||||||
|
|
|
@ -35,8 +35,8 @@ func (b *Blobovniczas) Exists(ctx context.Context, prm common.ExistsPrm) (common
|
||||||
defer span.End()
|
defer span.End()
|
||||||
|
|
||||||
if prm.StorageID != nil {
|
if prm.StorageID != nil {
|
||||||
id := blobovnicza.NewIDFromBytes(prm.StorageID)
|
id := NewIDFromBytes(prm.StorageID)
|
||||||
shBlz := b.getBlobovnicza(id.String())
|
shBlz := b.getBlobovnicza(id.Path())
|
||||||
blz, err := shBlz.Open()
|
blz, err := shBlz.Open()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return common.ExistsRes{}, err
|
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
|
// 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.MkdirAll(filepath.Join(dir, relBadFileDir), os.ModePerm))
|
||||||
require.NoError(t, os.WriteFile(filepath.Join(dir, relBadFileDir, badFileName), []byte("not a boltdb file content"), 0777))
|
require.NoError(t, os.WriteFile(filepath.Join(dir, relBadFileDir, badFileName+".db"), []byte("not a boltdb file content"), 0777))
|
||||||
|
|
||||||
res, err := b.Exists(context.Background(), common.ExistsPrm{Address: addr, StorageID: []byte(filepath.Join(relBadFileDir, badFileName))})
|
res, err := b.Exists(context.Background(), common.ExistsPrm{Address: addr, StorageID: []byte(filepath.Join(relBadFileDir, badFileName))})
|
||||||
require.Error(t, err)
|
require.Error(t, err)
|
||||||
|
|
|
@ -46,8 +46,8 @@ func (b *Blobovniczas) Get(ctx context.Context, prm common.GetPrm) (res common.G
|
||||||
bPrm.SetAddress(prm.Address)
|
bPrm.SetAddress(prm.Address)
|
||||||
|
|
||||||
if prm.StorageID != nil {
|
if prm.StorageID != nil {
|
||||||
id := blobovnicza.NewIDFromBytes(prm.StorageID)
|
id := NewIDFromBytes(prm.StorageID)
|
||||||
shBlz := b.getBlobovnicza(id.String())
|
shBlz := b.getBlobovnicza(id.Path())
|
||||||
blz, err := shBlz.Open()
|
blz, err := shBlz.Open()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return res, err
|
return res, err
|
||||||
|
|
|
@ -45,8 +45,8 @@ func (b *Blobovniczas) GetRange(ctx context.Context, prm common.GetRangePrm) (re
|
||||||
defer span.End()
|
defer span.End()
|
||||||
|
|
||||||
if prm.StorageID != nil {
|
if prm.StorageID != nil {
|
||||||
id := blobovnicza.NewIDFromBytes(prm.StorageID)
|
id := NewIDFromBytes(prm.StorageID)
|
||||||
shBlz := b.getBlobovnicza(id.String())
|
shBlz := b.getBlobovnicza(id.Path())
|
||||||
blz, err := shBlz.Open()
|
blz, err := shBlz.Open()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return common.GetRangeRes{}, err
|
return common.GetRangeRes{}, err
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
package blobovnicza
|
package blobovniczatree
|
||||||
|
|
||||||
// ID represents Blobovnicza identifier.
|
// ID represents Blobovnicza identifier.
|
||||||
type ID []byte
|
type ID []byte
|
||||||
|
@ -8,8 +8,8 @@ func NewIDFromBytes(v []byte) *ID {
|
||||||
return (*ID)(&v)
|
return (*ID)(&v)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (id ID) String() string {
|
func (id ID) Path() string {
|
||||||
return string(id)
|
return string(id) + dbExtension
|
||||||
}
|
}
|
||||||
|
|
||||||
func (id ID) Bytes() []byte {
|
func (id ID) Bytes() []byte {
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/blobovnicza"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/blobovnicza"
|
||||||
|
@ -50,7 +51,7 @@ func (b *Blobovniczas) Iterate(ctx context.Context, prm common.IteratePrm) (comm
|
||||||
return prm.Handler(common.IterationElement{
|
return prm.Handler(common.IterationElement{
|
||||||
Address: elem.Address(),
|
Address: elem.Address(),
|
||||||
ObjectData: data,
|
ObjectData: data,
|
||||||
StorageID: []byte(p),
|
StorageID: []byte(strings.TrimSuffix(p, dbExtension)),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
return prm.LazyHandler(elem.Address(), func() ([]byte, error) {
|
return prm.LazyHandler(elem.Address(), func() ([]byte, error) {
|
||||||
|
@ -134,10 +135,16 @@ func (b *Blobovniczas) iterateSorted(ctx context.Context, addr *oid.Address, cur
|
||||||
return false, ctx.Err()
|
return false, ctx.Err()
|
||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
|
|
||||||
|
lastPart := u64ToHexString(indices[i])
|
||||||
|
if isLeafLevel {
|
||||||
|
lastPart = u64ToHexStringExt(indices[i])
|
||||||
|
}
|
||||||
|
|
||||||
if i == 0 {
|
if i == 0 {
|
||||||
curPath = append(curPath, u64ToHexString(indices[i]))
|
curPath = append(curPath, lastPart)
|
||||||
} else {
|
} else {
|
||||||
curPath[len(curPath)-1] = u64ToHexString(indices[i])
|
curPath[len(curPath)-1] = lastPart
|
||||||
}
|
}
|
||||||
|
|
||||||
if exec {
|
if exec {
|
||||||
|
|
|
@ -115,7 +115,7 @@ func newLevelDBManager(width uint64, options []blobovnicza.Option, rootPath stri
|
||||||
databases: make([]*sharedDB, width),
|
databases: make([]*sharedDB, width),
|
||||||
}
|
}
|
||||||
for idx := uint64(0); idx < width; idx++ {
|
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
|
return result
|
||||||
}
|
}
|
||||||
|
|
|
@ -69,7 +69,7 @@ func (b *Blobovniczas) Put(ctx context.Context, prm common.PutPrm) (common.PutRe
|
||||||
|
|
||||||
type putIterator struct {
|
type putIterator struct {
|
||||||
B *Blobovniczas
|
B *Blobovniczas
|
||||||
ID *blobovnicza.ID
|
ID *ID
|
||||||
AllFull bool
|
AllFull bool
|
||||||
PutPrm blobovnicza.PutPrm
|
PutPrm blobovnicza.PutPrm
|
||||||
}
|
}
|
||||||
|
@ -109,7 +109,7 @@ func (i *putIterator) iterate(ctx context.Context, lvlPath string) (bool, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
idx := u64FromHexString(filepath.Base(active.Path()))
|
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
|
return true, nil
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue