forked from TrueCloudLab/frostfs-node
[#661] blobovniczatree: Allow to change depth or width
Now it is possible to change depth or with of blobovniczatree. Signed-off-by: Dmitrii Stepanov <d.stepanov@yadro.com>
This commit is contained in:
parent
484eb59893
commit
c1667a11d2
10 changed files with 355 additions and 27 deletions
|
@ -81,7 +81,7 @@ func NewBlobovniczaTree(opts ...Option) (blz *Blobovniczas) {
|
||||||
blz.blzLeafWidth = blz.blzShallowWidth
|
blz.blzLeafWidth = blz.blzShallowWidth
|
||||||
}
|
}
|
||||||
|
|
||||||
blz.commondbManager = newDBManager(blz.rootPath, blz.blzOpts, blz.blzLeafWidth, blz.readOnly, blz.metrics.Blobovnicza(), blz.log)
|
blz.commondbManager = newDBManager(blz.rootPath, blz.blzOpts, blz.readOnly, blz.metrics.Blobovnicza(), blz.log)
|
||||||
blz.activeDBManager = newActiveDBManager(blz.commondbManager, blz.blzLeafWidth)
|
blz.activeDBManager = newActiveDBManager(blz.commondbManager, blz.blzLeafWidth)
|
||||||
blz.dbCache = newDBCache(blz.openedCacheSize, blz.commondbManager)
|
blz.dbCache = newDBCache(blz.openedCacheSize, blz.commondbManager)
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/internal/logs"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/internal/logs"
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/util"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -39,7 +40,36 @@ func (b *Blobovniczas) Init() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return b.iterateLeaves(context.TODO(), func(p string) (bool, error) {
|
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
|
||||||
|
}
|
||||||
shBlz := b.getBlobovniczaWithoutCaching(p)
|
shBlz := b.getBlobovniczaWithoutCaching(p)
|
||||||
_, err := shBlz.Open()
|
_, err := shBlz.Open()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -1,12 +1,16 @@
|
||||||
package blobovniczatree
|
package blobovniczatree
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
objectCore "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/core/object"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/blobovnicza"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/blobovnicza"
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/blobstor/common"
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/blobstor/internal/blobstortest"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -67,3 +71,119 @@ func validateTestTree(t *testing.T, path string) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestObjectsAvailableAfterDepthAndWidthEdit(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
rootDir := t.TempDir()
|
||||||
|
|
||||||
|
blz := NewBlobovniczaTree(
|
||||||
|
WithBlobovniczaShallowDepth(3),
|
||||||
|
WithBlobovniczaShallowWidth(5),
|
||||||
|
WithRootPath(rootDir),
|
||||||
|
)
|
||||||
|
|
||||||
|
require.NoError(t, blz.Open(false))
|
||||||
|
require.NoError(t, blz.Init())
|
||||||
|
|
||||||
|
obj35 := blobstortest.NewObject(10 * 1024)
|
||||||
|
addr35 := objectCore.AddressOf(obj35)
|
||||||
|
raw, err := obj35.Marshal()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
pRes35, err := blz.Put(context.Background(), common.PutPrm{
|
||||||
|
Address: addr35,
|
||||||
|
Object: obj35,
|
||||||
|
RawData: raw,
|
||||||
|
})
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
gRes, err := blz.Get(context.Background(), common.GetPrm{
|
||||||
|
Address: addr35,
|
||||||
|
StorageID: pRes35.StorageID,
|
||||||
|
})
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.EqualValues(t, obj35, gRes.Object)
|
||||||
|
|
||||||
|
gRes, err = blz.Get(context.Background(), common.GetPrm{
|
||||||
|
Address: addr35,
|
||||||
|
})
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.EqualValues(t, obj35, gRes.Object)
|
||||||
|
|
||||||
|
require.NoError(t, blz.Close())
|
||||||
|
|
||||||
|
// change depth and width
|
||||||
|
blz = NewBlobovniczaTree(
|
||||||
|
WithBlobovniczaShallowDepth(5),
|
||||||
|
WithBlobovniczaShallowWidth(2),
|
||||||
|
WithRootPath(rootDir),
|
||||||
|
)
|
||||||
|
|
||||||
|
require.NoError(t, blz.Open(false))
|
||||||
|
require.NoError(t, blz.Init())
|
||||||
|
|
||||||
|
gRes, err = blz.Get(context.Background(), common.GetPrm{
|
||||||
|
Address: addr35,
|
||||||
|
StorageID: pRes35.StorageID,
|
||||||
|
})
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.EqualValues(t, obj35, gRes.Object)
|
||||||
|
|
||||||
|
gRes, err = blz.Get(context.Background(), common.GetPrm{
|
||||||
|
Address: addr35,
|
||||||
|
})
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.EqualValues(t, obj35, gRes.Object)
|
||||||
|
|
||||||
|
obj52 := blobstortest.NewObject(10 * 1024)
|
||||||
|
addr52 := objectCore.AddressOf(obj52)
|
||||||
|
raw, err = obj52.Marshal()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
pRes52, err := blz.Put(context.Background(), common.PutPrm{
|
||||||
|
Address: addr52,
|
||||||
|
Object: obj52,
|
||||||
|
RawData: raw,
|
||||||
|
})
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
require.NoError(t, blz.Close())
|
||||||
|
|
||||||
|
// change depth and width back
|
||||||
|
blz = NewBlobovniczaTree(
|
||||||
|
WithBlobovniczaShallowDepth(3),
|
||||||
|
WithBlobovniczaShallowWidth(5),
|
||||||
|
WithRootPath(rootDir),
|
||||||
|
)
|
||||||
|
require.NoError(t, blz.Open(false))
|
||||||
|
require.NoError(t, blz.Init())
|
||||||
|
|
||||||
|
gRes, err = blz.Get(context.Background(), common.GetPrm{
|
||||||
|
Address: addr35,
|
||||||
|
StorageID: pRes35.StorageID,
|
||||||
|
})
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.EqualValues(t, obj35, gRes.Object)
|
||||||
|
|
||||||
|
gRes, err = blz.Get(context.Background(), common.GetPrm{
|
||||||
|
Address: addr35,
|
||||||
|
})
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.EqualValues(t, obj35, gRes.Object)
|
||||||
|
|
||||||
|
gRes, err = blz.Get(context.Background(), common.GetPrm{
|
||||||
|
Address: addr52,
|
||||||
|
StorageID: pRes52.StorageID,
|
||||||
|
})
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.EqualValues(t, obj52, gRes.Object)
|
||||||
|
|
||||||
|
gRes, err = blz.Get(context.Background(), common.GetPrm{
|
||||||
|
Address: addr52,
|
||||||
|
})
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.EqualValues(t, obj52, gRes.Object)
|
||||||
|
|
||||||
|
require.NoError(t, blz.Close())
|
||||||
|
}
|
||||||
|
|
|
@ -63,7 +63,7 @@ func (b *Blobovniczas) Delete(ctx context.Context, prm common.DeletePrm) (res co
|
||||||
|
|
||||||
objectFound := false
|
objectFound := false
|
||||||
|
|
||||||
err = b.iterateSortedLeaves(ctx, &prm.Address, func(p string) (bool, error) {
|
err = b.iterateSortedDBPaths(ctx, prm.Address, func(p string) (bool, error) {
|
||||||
res, err = b.deleteObjectFromLevel(ctx, bPrm, p)
|
res, err = b.deleteObjectFromLevel(ctx, bPrm, p)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if !client.IsErrObjectNotFound(err) {
|
if !client.IsErrObjectNotFound(err) {
|
||||||
|
|
|
@ -51,7 +51,7 @@ func (b *Blobovniczas) Exists(ctx context.Context, prm common.ExistsPrm) (common
|
||||||
var gPrm blobovnicza.GetPrm
|
var gPrm blobovnicza.GetPrm
|
||||||
gPrm.SetAddress(prm.Address)
|
gPrm.SetAddress(prm.Address)
|
||||||
|
|
||||||
err := b.iterateSortedLeaves(ctx, &prm.Address, func(p string) (bool, error) {
|
err := b.iterateSortedDBPaths(ctx, prm.Address, func(p string) (bool, error) {
|
||||||
_, err := b.getObjectFromLevel(ctx, gPrm, p)
|
_, err := b.getObjectFromLevel(ctx, gPrm, p)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if !client.IsErrObjectNotFound(err) {
|
if !client.IsErrObjectNotFound(err) {
|
||||||
|
|
|
@ -63,7 +63,7 @@ func (b *Blobovniczas) Get(ctx context.Context, prm common.GetPrm) (res common.G
|
||||||
return res, err
|
return res, err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = b.iterateSortedLeaves(ctx, &prm.Address, func(p string) (bool, error) {
|
err = b.iterateSortedDBPaths(ctx, prm.Address, func(p string) (bool, error) {
|
||||||
res, err = b.getObjectFromLevel(ctx, bPrm, p)
|
res, err = b.getObjectFromLevel(ctx, bPrm, p)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if !client.IsErrObjectNotFound(err) {
|
if !client.IsErrObjectNotFound(err) {
|
||||||
|
|
|
@ -64,7 +64,7 @@ func (b *Blobovniczas) GetRange(ctx context.Context, prm common.GetRangePrm) (re
|
||||||
|
|
||||||
objectFound := false
|
objectFound := false
|
||||||
|
|
||||||
err = b.iterateSortedLeaves(ctx, &prm.Address, func(p string) (bool, error) {
|
err = b.iterateSortedDBPaths(ctx, prm.Address, func(p string) (bool, error) {
|
||||||
res, err = b.getRangeFromLevel(ctx, prm, p)
|
res, err = b.getRangeFromLevel(ctx, prm, p)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
outOfBounds := isErrOutOfRange(err)
|
outOfBounds := isErrOutOfRange(err)
|
||||||
|
|
|
@ -3,6 +3,7 @@ package blobovniczatree
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
@ -70,7 +71,7 @@ func (b *Blobovniczas) Iterate(ctx context.Context, prm common.IteratePrm) (comm
|
||||||
|
|
||||||
// iterator over all Blobovniczas in unsorted order. Break on f's error return.
|
// iterator over all Blobovniczas in unsorted order. Break on f's error return.
|
||||||
func (b *Blobovniczas) iterateBlobovniczas(ctx context.Context, ignoreErrors bool, f func(string, *blobovnicza.Blobovnicza) error) error {
|
func (b *Blobovniczas) iterateBlobovniczas(ctx context.Context, ignoreErrors bool, f func(string, *blobovnicza.Blobovnicza) error) error {
|
||||||
return b.iterateLeaves(ctx, func(p string) (bool, error) {
|
return b.iterateExistingDBPaths(ctx, func(p string) (bool, error) {
|
||||||
shBlz := b.getBlobovnicza(p)
|
shBlz := b.getBlobovnicza(p)
|
||||||
blz, err := shBlz.Open()
|
blz, err := shBlz.Open()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -91,7 +92,9 @@ func (b *Blobovniczas) iterateBlobovniczas(ctx context.Context, ignoreErrors boo
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// iterator over the paths of Blobovniczas sorted by weight.
|
// iterateSortedLeaves iterates over the paths of Blobovniczas sorted by weight.
|
||||||
|
//
|
||||||
|
// Uses depth, width and leaf width for iteration.
|
||||||
func (b *Blobovniczas) iterateSortedLeaves(ctx context.Context, addr *oid.Address, f func(string) (bool, error)) error {
|
func (b *Blobovniczas) iterateSortedLeaves(ctx context.Context, addr *oid.Address, f func(string) (bool, error)) error {
|
||||||
_, err := b.iterateSorted(
|
_, err := b.iterateSorted(
|
||||||
ctx,
|
ctx,
|
||||||
|
@ -169,9 +172,105 @@ func (b *Blobovniczas) iterateSorted(ctx context.Context, addr *oid.Address, cur
|
||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// iterator over the paths of Blobovniczas in random order.
|
// iterateExistingDBPaths iterates over the paths of Blobovniczas without any order.
|
||||||
func (b *Blobovniczas) iterateLeaves(ctx context.Context, f func(string) (bool, error)) error {
|
//
|
||||||
return b.iterateSortedLeaves(ctx, nil, f)
|
// Uses existed blobovnicza files for iteration.
|
||||||
|
func (b *Blobovniczas) iterateExistingDBPaths(ctx context.Context, f func(string) (bool, error)) error {
|
||||||
|
_, err := b.iterateExistingDBPathsDFS(ctx, "", f)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Blobovniczas) iterateExistingDBPathsDFS(ctx context.Context, path string, f func(string) (bool, error)) (bool, error) {
|
||||||
|
sysPath := filepath.Join(b.rootPath, path)
|
||||||
|
entries, err := os.ReadDir(sysPath)
|
||||||
|
if os.IsNotExist(err) && b.readOnly && path == "" { // non initialized tree in read only mode
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
for _, entry := range entries {
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return false, ctx.Err()
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
if entry.IsDir() {
|
||||||
|
stop, err := b.iterateExistingDBPathsDFS(ctx, filepath.Join(path, entry.Name()), f)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
if stop {
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
stop, err := f(filepath.Join(path, entry.Name()))
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
if stop {
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Blobovniczas) iterateSortedDBPaths(ctx context.Context, addr oid.Address, f func(string) (bool, error)) error {
|
||||||
|
_, err := b.iterateSordedDBPathsInternal(ctx, "", addr, f)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Blobovniczas) iterateSordedDBPathsInternal(ctx context.Context, path string, addr oid.Address, f func(string) (bool, error)) (bool, error) {
|
||||||
|
sysPath := filepath.Join(b.rootPath, path)
|
||||||
|
entries, err := os.ReadDir(sysPath)
|
||||||
|
if os.IsNotExist(err) && b.readOnly && path == "" { // non initialized tree in read only mode
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
var dbIdxs []uint64
|
||||||
|
var dirIdxs []uint64
|
||||||
|
|
||||||
|
for _, entry := range entries {
|
||||||
|
idx := u64FromHexString(entry.Name())
|
||||||
|
if entry.IsDir() {
|
||||||
|
dirIdxs = append(dirIdxs, idx)
|
||||||
|
} else {
|
||||||
|
dbIdxs = append(dbIdxs, idx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(dbIdxs) > 0 {
|
||||||
|
hrw.SortSliceByValue(dbIdxs, addressHash(&addr, path))
|
||||||
|
for _, dbIdx := range dbIdxs {
|
||||||
|
dbPath := filepath.Join(path, u64ToHexStringExt(dbIdx))
|
||||||
|
stop, err := f(dbPath)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
if stop {
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(dirIdxs) > 0 {
|
||||||
|
hrw.SortSliceByValue(dirIdxs, addressHash(&addr, path))
|
||||||
|
for _, dirIdx := range dirIdxs {
|
||||||
|
dirPath := filepath.Join(path, u64ToHexString(dirIdx))
|
||||||
|
stop, err := b.iterateSordedDBPathsInternal(ctx, dirPath, addr, f)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
if stop {
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// makes slice of uint64 values from 0 to number-1.
|
// makes slice of uint64 values from 0 to number-1.
|
||||||
|
|
|
@ -0,0 +1,41 @@
|
||||||
|
package blobovniczatree
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
oidtest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id/test"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestIterateSortedLeavesAndDBPathsAreSame(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
blz := NewBlobovniczaTree(
|
||||||
|
WithBlobovniczaShallowDepth(3),
|
||||||
|
WithBlobovniczaShallowWidth(5),
|
||||||
|
WithRootPath(t.TempDir()),
|
||||||
|
)
|
||||||
|
require.NoError(t, blz.Open(false))
|
||||||
|
require.NoError(t, blz.Init())
|
||||||
|
defer func() {
|
||||||
|
require.NoError(t, blz.Close())
|
||||||
|
}()
|
||||||
|
|
||||||
|
addr := oidtest.Address()
|
||||||
|
|
||||||
|
var leaves []string
|
||||||
|
var dbPaths []string
|
||||||
|
|
||||||
|
blz.iterateSortedLeaves(context.Background(), &addr, func(s string) (bool, error) {
|
||||||
|
leaves = append(leaves, s)
|
||||||
|
return false, nil
|
||||||
|
})
|
||||||
|
|
||||||
|
blz.iterateSortedDBPaths(context.Background(), addr, func(s string) (bool, error) {
|
||||||
|
dbPaths = append(dbPaths, s)
|
||||||
|
return false, nil
|
||||||
|
})
|
||||||
|
|
||||||
|
require.Equal(t, leaves, dbPaths)
|
||||||
|
}
|
|
@ -107,25 +107,65 @@ func (b *sharedDB) Path() string {
|
||||||
|
|
||||||
// levelDbManager stores pointers of the sharedDB's for the leaf directory of the blobovnicza tree.
|
// levelDbManager stores pointers of the sharedDB's for the leaf directory of the blobovnicza tree.
|
||||||
type levelDbManager struct {
|
type levelDbManager struct {
|
||||||
databases []*sharedDB
|
dbMtx *sync.RWMutex
|
||||||
|
databases map[uint64]*sharedDB
|
||||||
|
|
||||||
|
options []blobovnicza.Option
|
||||||
|
path string
|
||||||
|
readOnly bool
|
||||||
|
metrics blobovnicza.Metrics
|
||||||
|
openDBCounter *openDBCounter
|
||||||
|
closedFlag *atomic.Bool
|
||||||
|
log *logger.Logger
|
||||||
}
|
}
|
||||||
|
|
||||||
func newLevelDBManager(width uint64, options []blobovnicza.Option, rootPath string, lvlPath string,
|
func newLevelDBManager(options []blobovnicza.Option, rootPath string, lvlPath string,
|
||||||
readOnly bool, metrics blobovnicza.Metrics, openDBCounter *openDBCounter, closedFlog *atomic.Bool, log *logger.Logger,
|
readOnly bool, metrics blobovnicza.Metrics, openDBCounter *openDBCounter, closedFlag *atomic.Bool, log *logger.Logger,
|
||||||
) *levelDbManager {
|
) *levelDbManager {
|
||||||
result := &levelDbManager{
|
result := &levelDbManager{
|
||||||
databases: make([]*sharedDB, width),
|
databases: make(map[uint64]*sharedDB),
|
||||||
}
|
dbMtx: &sync.RWMutex{},
|
||||||
for idx := uint64(0); idx < width; idx++ {
|
|
||||||
result.databases[idx] = newSharedDB(options, filepath.Join(rootPath, lvlPath, u64ToHexStringExt(idx)), readOnly, metrics, openDBCounter, closedFlog, log)
|
options: options,
|
||||||
|
path: filepath.Join(rootPath, lvlPath),
|
||||||
|
readOnly: readOnly,
|
||||||
|
metrics: metrics,
|
||||||
|
openDBCounter: openDBCounter,
|
||||||
|
closedFlag: closedFlag,
|
||||||
|
log: log,
|
||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *levelDbManager) GetByIndex(idx uint64) *sharedDB {
|
func (m *levelDbManager) GetByIndex(idx uint64) *sharedDB {
|
||||||
|
res := m.getDBIfExists(idx)
|
||||||
|
if res != nil {
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
return m.getOrCreateDB(idx)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *levelDbManager) getDBIfExists(idx uint64) *sharedDB {
|
||||||
|
m.dbMtx.RLock()
|
||||||
|
defer m.dbMtx.RUnlock()
|
||||||
|
|
||||||
return m.databases[idx]
|
return m.databases[idx]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *levelDbManager) getOrCreateDB(idx uint64) *sharedDB {
|
||||||
|
m.dbMtx.Lock()
|
||||||
|
defer m.dbMtx.Unlock()
|
||||||
|
|
||||||
|
db := m.databases[idx]
|
||||||
|
if db != nil {
|
||||||
|
return db
|
||||||
|
}
|
||||||
|
|
||||||
|
db = newSharedDB(m.options, filepath.Join(m.path, u64ToHexStringExt(idx)), m.readOnly, m.metrics, m.openDBCounter, m.closedFlag, m.log)
|
||||||
|
m.databases[idx] = db
|
||||||
|
return db
|
||||||
|
}
|
||||||
|
|
||||||
// dbManager manages the opening and closing of blobovnicza instances.
|
// dbManager manages the opening and closing of blobovnicza instances.
|
||||||
//
|
//
|
||||||
// The blobovnicza opens at the first request, closes after the last request.
|
// The blobovnicza opens at the first request, closes after the last request.
|
||||||
|
@ -139,17 +179,15 @@ type dbManager struct {
|
||||||
options []blobovnicza.Option
|
options []blobovnicza.Option
|
||||||
readOnly bool
|
readOnly bool
|
||||||
metrics blobovnicza.Metrics
|
metrics blobovnicza.Metrics
|
||||||
leafWidth uint64
|
|
||||||
log *logger.Logger
|
log *logger.Logger
|
||||||
}
|
}
|
||||||
|
|
||||||
func newDBManager(rootPath string, options []blobovnicza.Option, leafWidth uint64, readOnly bool, metrics blobovnicza.Metrics, log *logger.Logger) *dbManager {
|
func newDBManager(rootPath string, options []blobovnicza.Option, readOnly bool, metrics blobovnicza.Metrics, log *logger.Logger) *dbManager {
|
||||||
return &dbManager{
|
return &dbManager{
|
||||||
rootPath: rootPath,
|
rootPath: rootPath,
|
||||||
options: options,
|
options: options,
|
||||||
readOnly: readOnly,
|
readOnly: readOnly,
|
||||||
metrics: metrics,
|
metrics: metrics,
|
||||||
leafWidth: leafWidth,
|
|
||||||
levelToManager: make(map[string]*levelDbManager),
|
levelToManager: make(map[string]*levelDbManager),
|
||||||
levelToManagerGuard: &sync.RWMutex{},
|
levelToManagerGuard: &sync.RWMutex{},
|
||||||
log: log,
|
log: log,
|
||||||
|
@ -197,7 +235,7 @@ func (m *dbManager) getOrCreateLevelManager(lvlPath string) *levelDbManager {
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
result := newLevelDBManager(m.leafWidth, m.options, m.rootPath, lvlPath, m.readOnly, m.metrics, m.dbCounter, m.closedFlag, m.log)
|
result := newLevelDBManager(m.options, m.rootPath, lvlPath, m.readOnly, m.metrics, m.dbCounter, m.closedFlag, m.log)
|
||||||
m.levelToManager[lvlPath] = result
|
m.levelToManager[lvlPath] = result
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue