forked from TrueCloudLab/frostfs-node
[#536] blobovnicza: Drop cache
Each blobovnicza instance is opened while is in use. Signed-off-by: Dmitrii Stepanov <d.stepanov@yadro.com>
This commit is contained in:
parent
b9b86d2ec8
commit
c672f59ab8
16 changed files with 586 additions and 474 deletions
243
pkg/local_object_storage/blobstor/blobovniczatree/manager.go
Normal file
243
pkg/local_object_storage/blobstor/blobovniczatree/manager.go
Normal file
|
@ -0,0 +1,243 @@
|
|||
package blobovniczatree
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-node/internal/logs"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/blobovnicza"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/util/logger"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
// sharedDB is responsible for opening and closing a file of single blobovnicza.
|
||||
type sharedDB struct {
|
||||
guard *sync.RWMutex
|
||||
blcza *blobovnicza.Blobovnicza
|
||||
refCount uint32
|
||||
|
||||
openDBCounter *openDBCounter
|
||||
closedFlag *atomic.Bool
|
||||
options []blobovnicza.Option
|
||||
path string
|
||||
readOnly bool
|
||||
metrics blobovnicza.Metrics
|
||||
log *logger.Logger
|
||||
}
|
||||
|
||||
func newSharedDB(options []blobovnicza.Option, path string, readOnly bool,
|
||||
metrics blobovnicza.Metrics, openDBCounter *openDBCounter, closedFlag *atomic.Bool, log *logger.Logger) *sharedDB {
|
||||
return &sharedDB{
|
||||
guard: &sync.RWMutex{},
|
||||
|
||||
options: options,
|
||||
path: path,
|
||||
readOnly: readOnly,
|
||||
metrics: metrics,
|
||||
closedFlag: closedFlag,
|
||||
log: log,
|
||||
openDBCounter: openDBCounter,
|
||||
}
|
||||
}
|
||||
|
||||
func (b *sharedDB) Open() (*blobovnicza.Blobovnicza, error) {
|
||||
if b.closedFlag.Load() {
|
||||
return nil, errClosed
|
||||
}
|
||||
|
||||
b.guard.Lock()
|
||||
defer b.guard.Unlock()
|
||||
|
||||
if b.refCount > 0 {
|
||||
b.refCount++
|
||||
return b.blcza, nil
|
||||
}
|
||||
|
||||
blz := blobovnicza.New(append(b.options,
|
||||
blobovnicza.WithReadOnly(b.readOnly),
|
||||
blobovnicza.WithPath(b.path),
|
||||
blobovnicza.WithMetrics(b.metrics),
|
||||
)...)
|
||||
|
||||
if err := blz.Open(); err != nil {
|
||||
return nil, fmt.Errorf("could not open blobovnicza %s: %w", b.path, err)
|
||||
}
|
||||
if err := blz.Init(); err != nil {
|
||||
return nil, fmt.Errorf("could not init blobovnicza %s: %w", b.path, err)
|
||||
}
|
||||
|
||||
b.refCount++
|
||||
b.blcza = blz
|
||||
b.openDBCounter.Inc()
|
||||
|
||||
return blz, nil
|
||||
}
|
||||
|
||||
func (b *sharedDB) Close() {
|
||||
b.guard.Lock()
|
||||
defer b.guard.Unlock()
|
||||
|
||||
if b.refCount == 0 {
|
||||
b.log.Error(logs.AttemtToCloseAlreadyClosedBlobovnicza, zap.String("id", b.path))
|
||||
return
|
||||
}
|
||||
|
||||
if b.refCount == 1 {
|
||||
b.refCount = 0
|
||||
if err := b.blcza.Close(); err != nil {
|
||||
b.log.Error(logs.BlobovniczatreeCouldNotCloseBlobovnicza,
|
||||
zap.String("id", b.path),
|
||||
zap.String("error", err.Error()),
|
||||
)
|
||||
}
|
||||
b.blcza = nil
|
||||
b.openDBCounter.Dec()
|
||||
return
|
||||
}
|
||||
|
||||
b.refCount--
|
||||
}
|
||||
|
||||
func (b *sharedDB) Path() string {
|
||||
return b.path
|
||||
}
|
||||
|
||||
// levelDbManager stores pointers of the sharedDB's for the leaf directory of the blobovnicza tree.
|
||||
type levelDbManager struct {
|
||||
databases []*sharedDB
|
||||
}
|
||||
|
||||
func newLevelDBManager(width uint64, options []blobovnicza.Option, rootPath string, lvlPath string,
|
||||
readOnly bool, metrics blobovnicza.Metrics, openDBCounter *openDBCounter, closedFlog *atomic.Bool, log *logger.Logger) *levelDbManager {
|
||||
result := &levelDbManager{
|
||||
databases: make([]*sharedDB, width),
|
||||
}
|
||||
var idx uint64
|
||||
for idx = 0; idx < width; idx++ {
|
||||
result.databases[idx] = newSharedDB(options, filepath.Join(rootPath, lvlPath, u64ToHexString(idx)), readOnly, metrics, openDBCounter, closedFlog, log)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func (m *levelDbManager) GetByIndex(idx uint64) *sharedDB {
|
||||
return m.databases[idx]
|
||||
}
|
||||
|
||||
// dbManager manages the opening and closing of blobovnicza instances.
|
||||
//
|
||||
// The blobovnicza opens at the first request, closes after the last request.
|
||||
type dbManager struct {
|
||||
levelToManager map[string]*levelDbManager
|
||||
levelToManagerGuard *sync.RWMutex
|
||||
closedFlag *atomic.Bool
|
||||
dbCounter *openDBCounter
|
||||
|
||||
rootPath string
|
||||
options []blobovnicza.Option
|
||||
readOnly bool
|
||||
metrics blobovnicza.Metrics
|
||||
leafWidth uint64
|
||||
log *logger.Logger
|
||||
}
|
||||
|
||||
func newDBManager(rootPath string, options []blobovnicza.Option, leafWidth uint64, readOnly bool, metrics blobovnicza.Metrics, log *logger.Logger) *dbManager {
|
||||
return &dbManager{
|
||||
rootPath: rootPath,
|
||||
options: options,
|
||||
readOnly: readOnly,
|
||||
metrics: metrics,
|
||||
leafWidth: leafWidth,
|
||||
levelToManager: make(map[string]*levelDbManager),
|
||||
levelToManagerGuard: &sync.RWMutex{},
|
||||
log: log,
|
||||
closedFlag: &atomic.Bool{},
|
||||
dbCounter: newOpenDBCounter(),
|
||||
}
|
||||
}
|
||||
|
||||
func (m *dbManager) GetByPath(path string) *sharedDB {
|
||||
lvlPath := filepath.Dir(path)
|
||||
curIndex := u64FromHexString(filepath.Base(path))
|
||||
levelManager := m.getLevelManager(lvlPath)
|
||||
return levelManager.GetByIndex(curIndex)
|
||||
}
|
||||
|
||||
func (m *dbManager) Open() {
|
||||
m.closedFlag.Store(false)
|
||||
}
|
||||
|
||||
func (m *dbManager) Close() {
|
||||
m.closedFlag.Store(true)
|
||||
m.dbCounter.WaitUntilAllClosed()
|
||||
}
|
||||
|
||||
func (m *dbManager) getLevelManager(lvlPath string) *levelDbManager {
|
||||
result := m.getLevelManagerIfExists(lvlPath)
|
||||
if result != nil {
|
||||
return result
|
||||
}
|
||||
return m.getOrCreateLevelManager(lvlPath)
|
||||
}
|
||||
|
||||
func (m *dbManager) getLevelManagerIfExists(lvlPath string) *levelDbManager {
|
||||
m.levelToManagerGuard.RLock()
|
||||
defer m.levelToManagerGuard.RUnlock()
|
||||
|
||||
return m.levelToManager[lvlPath]
|
||||
}
|
||||
|
||||
func (m *dbManager) getOrCreateLevelManager(lvlPath string) *levelDbManager {
|
||||
m.levelToManagerGuard.Lock()
|
||||
defer m.levelToManagerGuard.Unlock()
|
||||
|
||||
if result, ok := m.levelToManager[lvlPath]; ok {
|
||||
return result
|
||||
}
|
||||
|
||||
result := newLevelDBManager(m.leafWidth, m.options, m.rootPath, lvlPath, m.readOnly, m.metrics, m.dbCounter, m.closedFlag, m.log)
|
||||
m.levelToManager[lvlPath] = result
|
||||
return result
|
||||
}
|
||||
|
||||
type openDBCounter struct {
|
||||
cond *sync.Cond
|
||||
count uint64
|
||||
}
|
||||
|
||||
func newOpenDBCounter() *openDBCounter {
|
||||
return &openDBCounter{
|
||||
cond: &sync.Cond{
|
||||
L: &sync.Mutex{},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (c *openDBCounter) Inc() {
|
||||
c.cond.L.Lock()
|
||||
defer c.cond.L.Unlock()
|
||||
|
||||
c.count++
|
||||
}
|
||||
|
||||
func (c *openDBCounter) Dec() {
|
||||
c.cond.L.Lock()
|
||||
defer c.cond.L.Unlock()
|
||||
|
||||
if c.count > 0 {
|
||||
c.count--
|
||||
}
|
||||
|
||||
if c.count == 0 {
|
||||
c.cond.Broadcast()
|
||||
}
|
||||
}
|
||||
|
||||
func (c *openDBCounter) WaitUntilAllClosed() {
|
||||
c.cond.L.Lock()
|
||||
for c.count > 0 {
|
||||
c.cond.Wait()
|
||||
}
|
||||
c.cond.L.Unlock()
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue