package blobovnicza import ( "fmt" "os" "path/filepath" "git.frostfs.info/TrueCloudLab/frostfs-node/internal/logs" "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/util" "go.etcd.io/bbolt" "go.uber.org/zap" ) // Open opens an internal database at the configured path with the configured permissions. // // If the database file does not exist, it will be created automatically. func (b *Blobovnicza) Open() error { b.log.Debug(logs.BlobovniczaCreatingDirectoryForBoltDB, zap.String("path", b.path), zap.Bool("ro", b.boltOptions.ReadOnly), ) var err error if !b.boltOptions.ReadOnly { err = util.MkdirAllX(filepath.Dir(b.path), b.perm) if err != nil { return err } } b.log.Debug(logs.BlobovniczaOpeningBoltDB, zap.String("path", b.path), zap.Stringer("permissions", b.perm), ) b.boltDB, err = bbolt.Open(b.path, b.perm, b.boltOptions) if err == nil { b.metrics.IncOpenBlobovnizcaCount() } return err } // Init initializes internal database structure. // // If Blobovnicza is already initialized, no action is taken. // // Should not be called in read-only configuration. func (b *Blobovnicza) Init() error { b.log.Debug(logs.BlobovniczaInitializing, zap.Uint64("object size limit", b.objSizeLimit), zap.Uint64("storage size limit", b.fullSizeLimit), ) if size := b.filled.Load(); size != 0 { b.log.Debug(logs.BlobovniczaAlreadyInitialized, zap.Uint64("size", size)) return nil } err := b.boltDB.Update(func(tx *bbolt.Tx) error { return b.iterateBucketKeys(func(lower, upper uint64, key []byte) (bool, error) { // create size range bucket rangeStr := stringifyBounds(lower, upper) b.log.Debug(logs.BlobovniczaCreatingBucketForSizeRange, zap.String("range", rangeStr)) _, err := tx.CreateBucketIfNotExists(key) if err != nil { return false, fmt.Errorf("(%T) could not create bucket for bounds %s: %w", b, rangeStr, err) } return false, nil }) }) if err != nil { return err } info, err := os.Stat(b.path) if err != nil { return fmt.Errorf("can't determine DB size: %w", err) } sz := uint64(info.Size()) b.filled.Store(sz) b.metrics.AddSize(sz) return err } // Close releases all internal database resources. func (b *Blobovnicza) Close() error { b.log.Debug(logs.BlobovniczaClosingBoltDB, zap.String("path", b.path), ) err := b.boltDB.Close() if err == nil { b.metrics.DecOpenBlobovnizcaCount() b.metrics.SubSize(b.filled.Load()) } return err }