Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ Changelog for NeoFS Node
- Support for creation of containers with initial placement policy (#3830)
- `neofs-lancet meta remove` command (#3891)
- `neofs-lancet fstree remove` command (#3892)
- SN has new `root`, `ts`, `lock`, `link`, `gc` object counter metrics (#3740)

### Fixed
- Resending the header after chunks have already been sent in object service `Get` handler (#3833)
Expand Down Expand Up @@ -40,13 +41,15 @@ Changelog for NeoFS Node
- 2.18 is the minimum supported version for new objects (#3869)
- Rename `neofs-lens` into `neofs-lancet` (#3894)
- SN no longer accepts objects with invocation or verification script bigger than 1KiB (#3887)
- Every object with non-zero payload is now paid, not only regular ones (#3856)

### Removed
- `node.persistent_sessions.path` config option from SN config (#3846)
- Undocumented ability to fetch the latest contract release for adm update-contracts (#3850)
- `storage.shards.resync_metabase` config option from SN config (#3849)
- One-time netmap contract placement migration routine for v0.49.0+ releases (#3821)
- Metabase v5 to v6 and v6 to v7 migrations (#3864)
- SN's `logic` object counter metric (#3740)

### Updated
- `github.com/nspcc-dev/neofs-sdk-go` module to `v1.0.0-rc.17.0.20260320132435-55419a28ca95` (#3785, #3817, #3808, #3830, #3883, #3896, #3887)
Expand Down
20 changes: 12 additions & 8 deletions pkg/local_object_storage/metabase/VERSION.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,11 @@ Numbers stand for a single byte value unless otherwise stated.
The lowest not used bucket index: 20.

### Primary buckets
- Container volume bucket
- Name: `3`
- Key: container ID
- Value: bucket with container metrics:
- `0` -> container size in bytes as little-endian uint64
- `1` -> container's objects number as little-endian uint64
- Bucket containing auxiliary information. All keys are custom and are not connected to the container
- Name: `5`
- Keys and values
- `id` -> shard id as bytes
- `version` -> metabase version as little-endian uint64
- `phy_counter` -> shard's physical object counter as little-endian uint64
- `logic_counter` -> shard's logical object counter as little-endian uint64
- `last_resync_epoch` -> last epoch when metabase was resynchronized as little-endian uint64
- Metadata bucket
- Name: `255` + container ID
Expand All @@ -34,9 +26,21 @@ The lowest not used bucket index: 20.
Presence means the whole container is scheduled for garbage collection.
- `5` + object ID \
Garbage mark for an object.
- `6` —> container's PHY objects counter as little-endian uint64
- `7` —> container's ROOT objects counter as little-endian uint64
- `8` —> container's TS objects counter as little-endian uint64
- `9` —> container's LOCK objects counter as little-endian uint64
- `10` —> container's LINK objects counter as little-endian uint64
- `11` —> container's garbage objects counter as little-endian uint64
- `12` —> container's non-GCed payload of physical objects counter as little-endian uint64

# History

## Version 10

Object counters are stored inside metadata bucket per container now. New ROOT,
TS, LOCK, LINK, garbage objects, available PHY objects payload counters added.

## Version 9

Dropped lockedPrefix, graveyard and tomoveit buckets (4, 0 and 2). Replaced
Expand Down
84 changes: 9 additions & 75 deletions pkg/local_object_storage/metabase/containers.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,34 +79,21 @@ func (db *DB) GetContainerInfo(id cid.ID) (ContainerInfo, error) {
}

func (db *DB) containerInfo(tx *bbolt.Tx, id cid.ID) ContainerInfo {
infoBkt := tx.Bucket(containerVolumeBucketName)
cnrBkt := infoBkt.Bucket(id[:])
if cnrBkt == nil {
metaBkt := tx.Bucket(metaBucketKey(id))
if metaBkt == nil || containerMarkedGC(metaBkt.Cursor()) {
return ContainerInfo{}
}

var res ContainerInfo
res.StorageSize = parseContainerCounter(cnrBkt.Get([]byte{containerStorageSizeKey}))
res.ObjectsNumber = parseContainerCounter(cnrBkt.Get([]byte{containerObjectsNumberKey}))
res.StorageSize = parseContainerCounter(metaBkt.Get([]byte{metaPrefixPayloadCounter}))

return res
}

func resetContainerSize(tx *bbolt.Tx, cID cid.ID) error {
infoBkt := tx.Bucket(containerVolumeBucketName)
cnrBkt := infoBkt.Bucket(cID[:])
if cnrBkt != nil {
err := cnrBkt.Put([]byte{containerStorageSizeKey}, make([]byte, 8))
if err != nil {
return fmt.Errorf("put zero storage size: %w", err)
}
err = cnrBkt.Put([]byte{containerObjectsNumberKey}, make([]byte, 8))
if err != nil {
return fmt.Errorf("put zero objects number: %w", err)
}
phy := parseContainerCounter(metaBkt.Get([]byte{metaPrefixPhyCounter}))
gc := parseContainerCounter(metaBkt.Get([]byte{metaPrefixGCCounter}))
if phy > gc {
res.ObjectsNumber = phy - gc
}

return nil
return res
}

func parseContainerCounter(v []byte) uint64 {
Expand All @@ -117,49 +104,6 @@ func parseContainerCounter(v []byte) uint64 {
return binary.LittleEndian.Uint64(v)
}

func changeContainerInfo(tx *bbolt.Tx, id cid.ID, storageSizeDelta, objectsNumberDelta int) error {
var err error
infoBkt := tx.Bucket(containerVolumeBucketName)
cnrInfoBkt := infoBkt.Bucket(id[:])
if cnrInfoBkt == nil {
cnrInfoBkt, err = infoBkt.CreateBucket(id[:])
if err != nil {
return fmt.Errorf("create container info: %w", err)
}
}

sizeOld := parseContainerCounter(cnrInfoBkt.Get([]byte{containerStorageSizeKey}))
sizeNew := changeCounter(sizeOld, storageSizeDelta)
buff := make([]byte, 8)
binary.LittleEndian.PutUint64(buff, sizeNew)
err = cnrInfoBkt.Put([]byte{containerStorageSizeKey}, buff)
if err != nil {
return fmt.Errorf("update container size value (from %d to %d): %w", sizeOld, sizeNew, err)
}

objsNumberOld := parseContainerCounter(cnrInfoBkt.Get([]byte{containerObjectsNumberKey}))
objsNumberNew := changeCounter(objsNumberOld, objectsNumberDelta)
buff = make([]byte, 8)
binary.LittleEndian.PutUint64(buff, objsNumberNew)
err = cnrInfoBkt.Put([]byte{containerObjectsNumberKey}, buff)
if err != nil {
return fmt.Errorf("update container objects number value (from %d to %d): %w", objsNumberOld, objsNumberNew, err)
}

return nil
}

func changeCounter(oldVal uint64, delta int) uint64 {
if delta >= 0 {
return oldVal + uint64(delta)
}
newVal := oldVal - uint64(-delta)
if newVal > oldVal {
return 0
}
return newVal
}

// DeleteContainer removes any information that the metabase has
// associated with the provided container (its objects) except
// the graveyard-related one.
Expand All @@ -173,18 +117,8 @@ func (db *DB) DeleteContainer(cID cid.ID) error {
return ErrReadOnlyMode
}

cIDRaw := cID[:]

return db.boltDB.Update(func(tx *bbolt.Tx) error {
// Estimations
bktEstimations := tx.Bucket(containerVolumeBucketName)
err := bktEstimations.DeleteBucket(cIDRaw)
if err != nil && !errors.Is(err, bolterrors.ErrBucketNotFound) {
return fmt.Errorf("estimations bucket cleanup: %w", err)
}

// Metadata
if err = tx.DeleteBucket(metaBucketKey(cID)); err != nil && !errors.Is(err, bolterrors.ErrBucketNotFound) {
if err := tx.DeleteBucket(metaBucketKey(cID)); err != nil && !errors.Is(err, bolterrors.ErrBucketNotFound) {
return fmt.Errorf("metadata bucket cleanup: %w", err)
}

Expand Down
9 changes: 3 additions & 6 deletions pkg/local_object_storage/metabase/containers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -156,16 +156,13 @@ func TestDB_ContainerSize(t *testing.T) {
require.Equal(t, N, int(info.ObjectsNumber))
}

t.Run("Inhume", func(t *testing.T) {
t.Run("Garbage", func(t *testing.T) {
for cnr, list := range objs {
volume := cids[cnr]

for i, obj := range list {
require.NoError(t, metaInhume(
db,
obj.Address(),
oidtest.Address(),
))
_, err := db.MarkGarbage(obj.Address())
require.NoError(t, err)

volume -= int(obj.PayloadSize())

Expand Down
8 changes: 1 addition & 7 deletions pkg/local_object_storage/metabase/control.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,8 +94,7 @@ func (db *DB) init(reset bool) error {
}

mStaticBuckets := map[string]struct{}{
string(containerVolumeBucketName): {},
string(shardInfoBucket): {},
string(shardInfoBucket): {},
}

if !reset {
Expand Down Expand Up @@ -260,11 +259,6 @@ func (db *DB) ResyncFromBlobstor(bs common.Storage, onIterationError func(oid.Ad
return fmt.Errorf("could not flush remaining objects to metabase: %w", err)
}

err = db.SyncCounters()
Comment thread
roman-khimov marked this conversation as resolved.
if err != nil {
return fmt.Errorf("could not sync object counters: %w", err)
}

return nil
}

Expand Down
Loading
Loading