Dmitrii Stepanov
d433b49265
All checks were successful
DCO action / DCO (pull_request) Successful in 2m40s
Vulncheck / Vulncheck (pull_request) Successful in 3m41s
Build / Build Components (1.20) (pull_request) Successful in 4m27s
Build / Build Components (1.21) (pull_request) Successful in 5m6s
Tests and linters / Staticcheck (pull_request) Successful in 6m16s
Tests and linters / gopls check (pull_request) Successful in 6m23s
Tests and linters / Lint (pull_request) Successful in 6m48s
Tests and linters / Tests (1.20) (pull_request) Successful in 9m4s
Tests and linters / Tests with -race (pull_request) Successful in 9m9s
Tests and linters / Tests (1.21) (pull_request) Successful in 9m23s
`fmt.Errorf can be replaced with errors.New` and `fmt.Sprintf can be replaced with string addition` Signed-off-by: Dmitrii Stepanov <d.stepanov@yadro.com>
154 lines
4.2 KiB
Go
154 lines
4.2 KiB
Go
package blobovniczatree
|
|
|
|
import (
|
|
"errors"
|
|
"strconv"
|
|
"strings"
|
|
"sync"
|
|
|
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/blobstor/common"
|
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/blobstor/compression"
|
|
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
|
|
"git.frostfs.info/TrueCloudLab/hrw"
|
|
)
|
|
|
|
// Blobovniczas represents the storage of the "small" objects.
|
|
//
|
|
// Each object is stored in Blobovnicza's (B-s).
|
|
// B-s are structured in a multilevel directory hierarchy
|
|
// with fixed depth and width (configured by BlobStor).
|
|
//
|
|
// Example (width = 4, depth = 3):
|
|
//
|
|
// x===============================x
|
|
// |[0] [1] [2] [3]|
|
|
// | \ / |
|
|
// | \ / |
|
|
// | \ / |
|
|
// | \ / |
|
|
// |[0] [1] [2] [3]|
|
|
// | | / |
|
|
// | | / |
|
|
// | | / |
|
|
// | | / |
|
|
// |[0](F) [1](A) [X] [X]|
|
|
// x===============================x
|
|
//
|
|
// Elements of the deepest level are B-s.
|
|
// B-s are allocated dynamically. At each moment of the time there is
|
|
// an active B (ex. A), set of already filled B-s (ex. F) and
|
|
// a list of not yet initialized B-s (ex. X). After filling the active B
|
|
// it becomes full, and next B becomes initialized and active.
|
|
//
|
|
// Active B and some of the full B-s are cached (LRU). All cached
|
|
// B-s are intitialized and opened.
|
|
//
|
|
// Object is saved as follows:
|
|
// 1. at each level, according to HRW, the next one is selected and
|
|
// dives into it until we reach the deepest;
|
|
// 2. at the B-s level object is saved to the active B. If active B
|
|
// is full, next B is opened, initialized and cached. If there
|
|
// is no more X candidates, goto 1 and process next level.
|
|
//
|
|
// After the object is saved in B, path concatenation is returned
|
|
// in system path format as B identifier (ex. "0/1/1" or "3/2/1").
|
|
type Blobovniczas struct {
|
|
cfg
|
|
|
|
commondbManager *dbManager
|
|
activeDBManager *activeDBManager
|
|
dbCache *dbCache
|
|
deleteProtectedObjects *addressMap
|
|
dbFilesGuard *sync.RWMutex
|
|
rebuildGuard *sync.RWMutex
|
|
}
|
|
|
|
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)
|
|
initConfig(&blz.cfg)
|
|
|
|
for i := range opts {
|
|
opts[i](&blz.cfg)
|
|
}
|
|
|
|
if blz.blzLeafWidth == 0 {
|
|
blz.blzLeafWidth = blz.blzShallowWidth
|
|
}
|
|
|
|
blz.commondbManager = newDBManager(blz.rootPath, blz.blzOpts, blz.readOnly, blz.metrics.Blobovnicza(), blz.log)
|
|
blz.activeDBManager = newActiveDBManager(blz.commondbManager, blz.blzLeafWidth)
|
|
blz.dbCache = newDBCache(blz.openedCacheSize, blz.commondbManager)
|
|
blz.deleteProtectedObjects = newAddressMap()
|
|
blz.dbFilesGuard = &sync.RWMutex{}
|
|
blz.rebuildGuard = &sync.RWMutex{}
|
|
|
|
return blz
|
|
}
|
|
|
|
// returns hash of the object address.
|
|
func addressHash(addr *oid.Address, path string) uint64 {
|
|
var a string
|
|
|
|
if addr != nil {
|
|
a = addr.EncodeToString()
|
|
}
|
|
|
|
return hrw.StringHash(a + path)
|
|
}
|
|
|
|
func u64ToHexString(ind uint64) string {
|
|
return strconv.FormatUint(ind, 16)
|
|
}
|
|
|
|
func u64ToHexStringExt(ind uint64) string {
|
|
return strconv.FormatUint(ind, 16) + dbExtension
|
|
}
|
|
|
|
func u64FromHexString(str string) uint64 {
|
|
v, err := strconv.ParseUint(strings.TrimSuffix(str, dbExtension), 16, 64)
|
|
if err != nil {
|
|
panic("blobovnicza name is not an index " + str)
|
|
}
|
|
|
|
return v
|
|
}
|
|
|
|
// Type is blobovniczatree storage type used in logs and configuration.
|
|
const Type = "blobovnicza"
|
|
|
|
// Type implements common.Storage.
|
|
func (b *Blobovniczas) Type() string {
|
|
return Type
|
|
}
|
|
|
|
// Path implements common.Storage.
|
|
func (b *Blobovniczas) Path() string {
|
|
return b.rootPath
|
|
}
|
|
|
|
// SetCompressor implements common.Storage.
|
|
func (b *Blobovniczas) SetCompressor(cc *compression.Config) {
|
|
b.compression = cc
|
|
}
|
|
|
|
func (b *Blobovniczas) Compressor() *compression.Config {
|
|
return b.compression
|
|
}
|
|
|
|
// SetReportErrorFunc implements common.Storage.
|
|
func (b *Blobovniczas) SetReportErrorFunc(f func(string, error)) {
|
|
b.reportError = f
|
|
}
|
|
|
|
func (b *Blobovniczas) SetParentID(parentID string) {
|
|
b.metrics.SetParentID(parentID)
|
|
}
|