diff --git a/CHANGELOG.md b/CHANGELOG.md index dee95bac87..0dad76425c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ Changelog for NeoFS Node - `neofs-cli object range` command now creates file with `rw-r--r--` permissions (#3544) - Alphabet nodes send basic storage income based on the new Reports API from `container` contract (#3053) - Use stream API of FSTree for object service `Get` operation (#3466) +- Use meta buckets to mark containers with GC (#3561) ### Removed - `neofs-cli object head --main-only` no-op flag (#3509) diff --git a/pkg/local_object_storage/metabase/VERSION.md b/pkg/local_object_storage/metabase/VERSION.md index 5f8f6cf008..ae2a93737d 100644 --- a/pkg/local_object_storage/metabase/VERSION.md +++ b/pkg/local_object_storage/metabase/VERSION.md @@ -16,10 +16,6 @@ The lowest not used bucket index: 20. - Name: `1` - Key: object address - Value: dummy value -- Garbage containers bucket - - Name: `17` - - Key: container ID - - Value: dummy value - Bucket containing IDs of objects that are candidates for moving to another shard. - Name: `2` @@ -51,6 +47,8 @@ The lowest not used bucket index: 20. Sign byte is 0 for negatives, 1 otherwise. Bits are inverted for negatives also. - `2` + attribute + `0x00` + value + `0x00` + object ID - `3` + object ID + attribute + `0x00` + value + - `4` — container-level GC mark. \ + Presence means the whole container is scheduled for garbage collection. # History @@ -59,6 +57,8 @@ The lowest not used bucket index: 20. Container statistic is now a bucket with multiple values. In the version number of objects was added +Drop garbage containers index (17), replaced with mark in metadata bucket. + ## Version 7 Fixed version 6 which could store OID keys in garbage object bucket. diff --git a/pkg/local_object_storage/metabase/containers.go b/pkg/local_object_storage/metabase/containers.go index 4af86804da..f50cdd4350 100644 --- a/pkg/local_object_storage/metabase/containers.go +++ b/pkg/local_object_storage/metabase/containers.go @@ -205,14 +205,6 @@ func (db *DB) DeleteContainer(cID cid.ID) error { return fmt.Errorf("metadata bucket cleanup: %w", err) } - cnrGCBkt := tx.Bucket(garbageContainersBucketName) - if cnrGCBkt != nil { - err = cnrGCBkt.Delete(cIDRaw) - if err != nil { - return fmt.Errorf("garbage containers cleanup: %w", err) - } - } - return nil }) } diff --git a/pkg/local_object_storage/metabase/control.go b/pkg/local_object_storage/metabase/control.go index 4d35988f75..0ca01e2db6 100644 --- a/pkg/local_object_storage/metabase/control.go +++ b/pkg/local_object_storage/metabase/control.go @@ -86,13 +86,12 @@ func (db *DB) init(reset bool) error { } mStaticBuckets := map[string]struct{}{ - string(containerVolumeBucketName): {}, - string(graveyardBucketName): {}, - string(toMoveItBucketName): {}, - string(garbageObjectsBucketName): {}, - string(garbageContainersBucketName): {}, - string(shardInfoBucket): {}, - string(bucketNameLocked): {}, + string(containerVolumeBucketName): {}, + string(graveyardBucketName): {}, + string(toMoveItBucketName): {}, + string(garbageObjectsBucketName): {}, + string(shardInfoBucket): {}, + string(bucketNameLocked): {}, } if !reset { diff --git a/pkg/local_object_storage/metabase/counter.go b/pkg/local_object_storage/metabase/counter.go index e646035b3b..5958c54342 100644 --- a/pkg/local_object_storage/metabase/counter.go +++ b/pkg/local_object_storage/metabase/counter.go @@ -140,7 +140,6 @@ func syncCounter(tx *bbolt.Tx, currEpoch uint64, force bool) error { graveyardBKT := tx.Bucket(graveyardBucketName) garbageObjectsBKT := tx.Bucket(garbageObjectsBucketName) - garbageContainersBKT := tx.Bucket(garbageContainersBucketName) key := make([]byte, addressKeySize) err = iteratePhyObjects(tx, func(cnr cid.ID, obj oid.ID) error { @@ -157,7 +156,7 @@ func syncCounter(tx *bbolt.Tx, currEpoch uint64, force bool) error { // check if an object is available: not with GCMark // and not covered with a tombstone - if inGraveyardWithKey(metaCursor, addressKey(addr, key), graveyardBKT, garbageObjectsBKT, garbageContainersBKT) == statusAvailable { + if inGraveyardWithKey(metaCursor, addressKey(addr, key), graveyardBKT, garbageObjectsBKT) == statusAvailable { logicCounter++ } diff --git a/pkg/local_object_storage/metabase/delete.go b/pkg/local_object_storage/metabase/delete.go index 95a400c093..545979c8fa 100644 --- a/pkg/local_object_storage/metabase/delete.go +++ b/pkg/local_object_storage/metabase/delete.go @@ -132,7 +132,6 @@ func (db *DB) delete(tx *bbolt.Tx, addr oid.Address, currEpoch uint64) (bool, bo cID := addr.Container() addrKey := addressKey(addr, key) garbageObjectsBKT := tx.Bucket(garbageObjectsBucketName) - garbageContainersBKT := tx.Bucket(garbageContainersBucketName) graveyardBKT := tx.Bucket(graveyardBucketName) metaBucket := tx.Bucket(metaBucketKey(cID)) var metaCursor *bbolt.Cursor @@ -140,7 +139,7 @@ func (db *DB) delete(tx *bbolt.Tx, addr oid.Address, currEpoch uint64) (bool, bo metaCursor = metaBucket.Cursor() } - removeAvailableObject := inGraveyardWithKey(metaCursor, addrKey, graveyardBKT, garbageObjectsBKT, garbageContainersBKT) == statusAvailable + removeAvailableObject := inGraveyardWithKey(metaCursor, addrKey, graveyardBKT, garbageObjectsBKT) == statusAvailable // remove record from the garbage bucket if garbageObjectsBKT != nil { diff --git a/pkg/local_object_storage/metabase/exists.go b/pkg/local_object_storage/metabase/exists.go index 4462c5c2fb..55bc988edc 100644 --- a/pkg/local_object_storage/metabase/exists.go +++ b/pkg/local_object_storage/metabase/exists.go @@ -145,15 +145,18 @@ func objectStatus(tx *bbolt.Tx, metaCursor *bbolt.Cursor, addr oid.Address, curr // where a single address needs to be checked. func inGraveyard(tx *bbolt.Tx, metaCursor *bbolt.Cursor, addr oid.Address) uint8 { var ( - addrKey = addressKey(addr, make([]byte, addressKeySize)) - garbageContainersBkt = tx.Bucket(garbageContainersBucketName) - garbageObjectsBkt = tx.Bucket(garbageObjectsBucketName) - graveyardBkt = tx.Bucket(graveyardBucketName) + addrKey = addressKey(addr, make([]byte, addressKeySize)) + garbageObjectsBkt = tx.Bucket(garbageObjectsBucketName) + graveyardBkt = tx.Bucket(graveyardBucketName) ) - return inGraveyardWithKey(metaCursor, addrKey, graveyardBkt, garbageObjectsBkt, garbageContainersBkt) + return inGraveyardWithKey(metaCursor, addrKey, graveyardBkt, garbageObjectsBkt) } -func inGraveyardWithKey(metaCursor *bbolt.Cursor, addrKey []byte, graveyard, garbageObjectsBCK, garbageContainersBCK *bbolt.Bucket) uint8 { +func inGraveyardWithKey(metaCursor *bbolt.Cursor, addrKey []byte, graveyard, garbageObjectsBCK *bbolt.Bucket) uint8 { + if metaCursor != nil && containerMarkedGC(metaCursor) { + return statusGCMarked + } + if associatedWithTypedObject(0, metaCursor, oid.ID(addrKey[cid.Size:]), objectSDK.TypeTombstone) { return statusTombstoned } @@ -171,11 +174,7 @@ func inGraveyardWithKey(metaCursor *bbolt.Cursor, addrKey []byte, graveyard, gar return statusAvailable } - val = garbageContainersBCK.Get(addrKey[:cidSize]) - if val == nil { - val = garbageObjectsBCK.Get(addrKey) - } - + val = garbageObjectsBCK.Get(addrKey) if val != nil { // object has been marked with GC return statusGCMarked diff --git a/pkg/local_object_storage/metabase/graveyard.go b/pkg/local_object_storage/metabase/graveyard.go index c1a9dba972..96d5903cba 100644 --- a/pkg/local_object_storage/metabase/graveyard.go +++ b/pkg/local_object_storage/metabase/graveyard.go @@ -288,7 +288,6 @@ func (db *DB) GetGarbage(limit int) ([]oid.Address, []cid.ID, error) { initCap := min(limit, reasonableLimit) var addrBuff oid.Address - var cidBuff cid.ID alreadyHandledContainers := make(map[cid.ID]struct{}) resObjects := make([]oid.Address, 0, initCap) resContainers := make([]cid.ID, 0) @@ -299,26 +298,34 @@ func (db *DB) GetGarbage(limit int) ([]oid.Address, []cid.ID, error) { // also be deleted as a part of non-existing // container so no need to handle it twice - bkt := tx.Bucket(garbageContainersBucketName) - c := bkt.Cursor() - - for k, _ := c.First(); k != nil; k, _ = c.Next() { - err := cidBuff.Decode(k) - if err != nil { - return fmt.Errorf("parsing raw CID: %w", err) + var inhumedCnrs []cid.ID + err := tx.ForEach(func(name []byte, b *bbolt.Bucket) error { + if name[0] == metadataPrefix && containerMarkedGC(b.Cursor()) { + var cnr cid.ID + cidRaw, prefix := parseContainerIDWithPrefix(&cnr, name) + if cidRaw == nil || prefix != metadataPrefix { + return nil + } + inhumedCnrs = append(inhumedCnrs, cnr) } + return nil + }) + if err != nil { + return fmt.Errorf("scanning inhumed containers: %w", err) + } - resObjects, err = listContainerObjects(tx, cidBuff, resObjects, limit) + for _, cnr := range inhumedCnrs { + resObjects, err = listContainerObjects(tx, cnr, resObjects, limit) if err != nil { - return fmt.Errorf("listing objects for %s container: %w", cidBuff, err) + return fmt.Errorf("listing objects for %s container: %w", cnr, err) } - alreadyHandledContainers[cidBuff] = struct{}{} + alreadyHandledContainers[cnr] = struct{}{} if len(resObjects) < limit { // all the objects from the container were listed, // container can be removed - resContainers = append(resContainers, cidBuff) + resContainers = append(resContainers, cnr) } else { return nil } @@ -327,8 +334,8 @@ func (db *DB) GetGarbage(limit int) ([]oid.Address, []cid.ID, error) { // deleted containers are not enough to reach the limit, // check manually deleted objects then - bkt = tx.Bucket(garbageObjectsBucketName) - c = bkt.Cursor() + bkt := tx.Bucket(garbageObjectsBucketName) + c := bkt.Cursor() for k, _ := c.First(); k != nil; k, _ = c.Next() { err := decodeAddressFromKey(&addrBuff, k) diff --git a/pkg/local_object_storage/metabase/inhume.go b/pkg/local_object_storage/metabase/inhume.go index 0a959ae459..4a8a858f07 100644 --- a/pkg/local_object_storage/metabase/inhume.go +++ b/pkg/local_object_storage/metabase/inhume.go @@ -58,7 +58,6 @@ func (db *DB) inhume(tombstone *oid.Address, tombExpiration uint64, force bool, ) err = db.boltDB.Update(func(tx *bbolt.Tx) error { garbageObjectsBKT := tx.Bucket(garbageObjectsBucketName) - garbageContainersBKT := tx.Bucket(garbageContainersBucketName) graveyardBKT := tx.Bucket(graveyardBucketName) var ( @@ -125,7 +124,7 @@ func (db *DB) inhume(tombstone *oid.Address, tombExpiration uint64, force bool, obj, err := get(tx, addr, false, true, currEpoch) targetKey := addressKey(addr, buf) if err == nil { - if inGraveyardWithKey(metaCursor, targetKey, graveyardBKT, garbageObjectsBKT, garbageContainersBKT) == statusAvailable { + if inGraveyardWithKey(metaCursor, targetKey, graveyardBKT, garbageObjectsBKT) == statusAvailable { // object is available, decrement the // logical counter inhumed++ @@ -219,13 +218,14 @@ func (db *DB) InhumeContainer(cID cid.ID) (uint64, error) { } var removedAvailable uint64 - rawCID := cID[:] err := db.boltDB.Update(func(tx *bbolt.Tx) error { - garbageContainersBKT := tx.Bucket(garbageContainersBucketName) - err := garbageContainersBKT.Put(rawCID, zeroValue) + metaBkt, err := tx.CreateBucketIfNotExists(metaBucketKey(cID)) if err != nil { - return fmt.Errorf("put GC mark for container: %w", err) + return fmt.Errorf("create meta bucket: %w", err) + } + if err := metaBkt.Put(containerGCMarkKey, nil); err != nil { + return fmt.Errorf("write container GC mark: %w", err) } info := db.containerInfo(tx, cID) diff --git a/pkg/local_object_storage/metabase/list.go b/pkg/local_object_storage/metabase/list.go index 0ba9f57e12..2a6b439abd 100644 --- a/pkg/local_object_storage/metabase/list.go +++ b/pkg/local_object_storage/metabase/list.go @@ -64,7 +64,6 @@ func (db *DB) listWithCursor(tx *bbolt.Tx, currEpoch uint64, result []objectcore var offset []byte graveyardBkt := tx.Bucket(graveyardBucketName) garbageObjectsBkt := tx.Bucket(garbageObjectsBucketName) - garbageContainersBkt := tx.Bucket(garbageContainersBucketName) var rawAddr = make([]byte, cidSize, addressKeySize) @@ -78,7 +77,7 @@ loop: bkt := tx.Bucket(name) if bkt != nil { copy(rawAddr, cidRaw) - result, offset, cursor = selectNFromBucket(bkt, currEpoch, graveyardBkt, garbageObjectsBkt, garbageContainersBkt, rawAddr, containerID, + result, offset, cursor = selectNFromBucket(bkt, currEpoch, graveyardBkt, garbageObjectsBkt, rawAddr, containerID, result, count, cursor, threshold) } bucketName = name @@ -112,7 +111,7 @@ loop: // object to start selecting from. Ignores inhumed objects. func selectNFromBucket(bkt *bbolt.Bucket, // main bucket currEpoch uint64, - graveyardBkt, garbageObjectsBkt, garbageContainersBkt *bbolt.Bucket, // cached graveyard buckets + graveyardBkt, garbageObjectsBkt *bbolt.Bucket, // cached graveyard buckets cidRaw []byte, // container ID prefix, optimization cnt cid.ID, // container ID to []objectcore.AddressWithType, // listing result @@ -132,6 +131,10 @@ func selectNFromBucket(bkt *bbolt.Bucket, // main bucket typePrefix = make([]byte, metaIDTypePrefixSize) ) + if containerMarkedGC(c) { + return to, nil, cursor + } + fillIDTypePrefix(typePrefix) if threshold { @@ -157,7 +160,7 @@ func selectNFromBucket(bkt *bbolt.Bucket, // main bucket mCursor := bkt.Cursor() offset = k - if inGraveyardWithKey(mCursor, append(cidRaw, obj[:]...), graveyardBkt, garbageObjectsBkt, garbageContainersBkt) != statusAvailable { + if inGraveyardWithKey(mCursor, append(cidRaw, obj[:]...), graveyardBkt, garbageObjectsBkt) != statusAvailable { continue } diff --git a/pkg/local_object_storage/metabase/metadata.go b/pkg/local_object_storage/metabase/metadata.go index 3c7a109aa6..b917803397 100644 --- a/pkg/local_object_storage/metabase/metadata.go +++ b/pkg/local_object_storage/metabase/metadata.go @@ -23,6 +23,7 @@ const ( metaPrefixAttrIDInt metaPrefixAttrIDPlain metaPrefixIDAttr + metaPrefixGC ) const ( diff --git a/pkg/local_object_storage/metabase/revive.go b/pkg/local_object_storage/metabase/revive.go index c011b857a8..b057fb80a9 100644 --- a/pkg/local_object_storage/metabase/revive.go +++ b/pkg/local_object_storage/metabase/revive.go @@ -85,7 +85,6 @@ func (db *DB) ReviveObject(addr oid.Address) (res ReviveStatus, err error) { err = db.boltDB.Update(func(tx *bbolt.Tx) error { garbageObjectsBKT := tx.Bucket(garbageObjectsBucketName) - garbageContainersBKT := tx.Bucket(garbageContainersBucketName) graveyardBKT := tx.Bucket(graveyardBucketName) buf := make([]byte, addressKeySize) @@ -98,6 +97,15 @@ func (db *DB) ReviveObject(addr oid.Address) (res ReviveStatus, err error) { return ErrObjectWasNotRemoved } + metaBucket := tx.Bucket(metaBucketKey(cnr)) + var metaCursor *bbolt.Cursor + if metaBucket != nil { + metaCursor = metaBucket.Cursor() + if containerMarkedGC(metaCursor) { + return ErrReviveFromContainerGarbage + } + } + val := graveyardBKT.Get(targetKey) if val != nil { // object in the graveyard @@ -112,11 +120,6 @@ func (db *DB) ReviveObject(addr oid.Address) (res ReviveStatus, err error) { res.setStatusGraveyard(tombAddress.EncodeToString()) res.tombstoneAddr = tombAddress } else { - val = garbageContainersBKT.Get(targetKey[:cidSize]) - if val != nil { - return ErrReviveFromContainerGarbage - } - val = garbageObjectsBKT.Get(targetKey) if val != nil { // object marked with GC mark @@ -127,12 +130,6 @@ func (db *DB) ReviveObject(addr oid.Address) (res ReviveStatus, err error) { return ErrObjectWasNotRemoved } - metaBucket := tx.Bucket(metaBucketKey(cnr)) - var metaCursor *bbolt.Cursor - if metaBucket != nil { - metaCursor = metaBucket.Cursor() - } - tombID, err := getTombstoneByAssociatedObject(metaCursor, addr.Object()) if err != nil { return fmt.Errorf("iterate covered by tombstones: %w", err) diff --git a/pkg/local_object_storage/metabase/status.go b/pkg/local_object_storage/metabase/status.go index 52c4b30602..3d557ab6e4 100644 --- a/pkg/local_object_storage/metabase/status.go +++ b/pkg/local_object_storage/metabase/status.go @@ -121,27 +121,6 @@ func readBuckets(tx *bbolt.Tx, cID cid.ID, oID oid.ID) ([]BucketValue, []HeaderF }) } - containerBuckets := []byte{ - garbageContainersPrefix, - } - - for _, bucketKey := range containerBuckets { - b := tx.Bucket([]byte{bucketKey}) - if b == nil { - continue - } - - v := b.Get(cIDRaw) - if v == nil { - continue - } - - oldIndexes = append(oldIndexes, BucketValue{ - BucketIndex: int(bucketKey), - Value: bytes.Clone(v), - }) - } - if b := tx.Bucket(bucketNameLocked); b != nil { b = b.Bucket(cIDRaw) if b != nil { diff --git a/pkg/local_object_storage/metabase/status_test.go b/pkg/local_object_storage/metabase/status_test.go index 9f7867588f..7c39a0fbba 100644 --- a/pkg/local_object_storage/metabase/status_test.go +++ b/pkg/local_object_storage/metabase/status_test.go @@ -99,15 +99,17 @@ func TestDB_ObjectStatus(t *testing.T) { }) t.Run("container marked as garbage", func(t *testing.T) { + obj.SetID(oidtest.OtherID(obj.GetID())) + require.NoError(t, db.Put(&obj)) + + addr := oid.NewAddress(obj.GetContainerID(), obj.GetID()) + _, err := db.InhumeContainer(obj.GetContainerID()) require.NoError(t, err) st, err := db.ObjectStatus(addr) require.NoError(t, err) - require.Contains(t, st.Buckets, meta.BucketValue{ - BucketIndex: 17, - Value: []byte{0xFF}, - }) + require.ElementsMatch(t, st.State, []string{"AVAILABLE", "GC MARKED"}) }) t.Run("moved", func(t *testing.T) { diff --git a/pkg/local_object_storage/metabase/util.go b/pkg/local_object_storage/metabase/util.go index c2a2021094..ed02489bb7 100644 --- a/pkg/local_object_storage/metabase/util.go +++ b/pkg/local_object_storage/metabase/util.go @@ -1,6 +1,7 @@ package meta import ( + "bytes" "crypto/sha256" "errors" "math/big" @@ -21,12 +22,12 @@ var ( graveyardBucketName = []byte{graveyardPrefix} // garbageObjectsBucketName stores rows with the objects that should be physically // deleted by the node (Garbage Collector routine). - garbageObjectsBucketName = []byte{garbageObjectsPrefix} - // garbageContainersBucketName stores rows with the containers that should be physically - // deleted by the node (Garbage Collector routine). - garbageContainersBucketName = []byte{garbageContainersPrefix} - toMoveItBucketName = []byte{toMoveItPrefix} - containerVolumeBucketName = []byte{containerVolumePrefix} + garbageObjectsBucketName = []byte{garbageObjectsPrefix} + toMoveItBucketName = []byte{toMoveItPrefix} + containerVolumeBucketName = []byte{containerVolumePrefix} + + // containerGCMarkKey marker key inside meta bucket designating whole container GC-marked. + containerGCMarkKey = []byte{metaPrefixGC} zeroValue = []byte{0xFF} ) @@ -58,9 +59,9 @@ const ( // shardInfoPrefix is used for storing shard ID. All keys are custom and are not connected to the container. shardInfoPrefix - //====================== + // ====================== // Unique index buckets. - //====================== + // ====================== // unusedPrimaryPrefix was deleted in metabase version 6 unusedPrimaryPrefix @@ -75,18 +76,18 @@ const ( // unusedRootPrefix was deleted in metabase version 5 unusedRootPrefix - //==================== + // ==================== // FKBT index buckets. - //==================== + // ==================== // unusedOwnerPrefix was deleted in metabase version 5 unusedOwnerPrefix // unusedUserAttributePrefix was deleted in metabase version 5 unusedUserAttributePrefix - //==================== + // ==================== // List index buckets. - //==================== + // ==================== // unusedPayloadHashPrefix was deleted in metabase version 5 unusedPayloadHashPrefix @@ -95,10 +96,8 @@ const ( // unusedSplitPrefix was deleted in metabase version 5 unusedSplitPrefix - // garbageContainersPrefix is used for the garbage containers bucket. - // Key: container ID - // Value: dummy value - garbageContainersPrefix + // unusedGarbageContainersPrefix was deleted in metabase version 8 + unusedGarbageContainersPrefix // unusedLinkObjectsPrefix was deleted in metabase version 6 unusedLinkObjectsPrefix @@ -189,3 +188,10 @@ func (x *keyBuffer) alloc(ln int) []byte { } return (*x)[:ln] } + +// containerMarkedGC checks if the container is marked by GC. +// metaCursor must be not nil. +func containerMarkedGC(metaCursor *bbolt.Cursor) bool { + k, _ := metaCursor.Seek(containerGCMarkKey) + return bytes.Equal(k, containerGCMarkKey) +} diff --git a/pkg/local_object_storage/metabase/version.go b/pkg/local_object_storage/metabase/version.go index 1d59c3fa86..43426e04b1 100644 --- a/pkg/local_object_storage/metabase/version.go +++ b/pkg/local_object_storage/metabase/version.go @@ -551,6 +551,38 @@ func migrateFrom7Version(db *DB) error { sizeRaw []byte } + // add GC marker key into meta buckets for containers already marked for GC + garbageContainersBucketName := []byte{unusedGarbageContainersPrefix} + garbageContainersBKT := tx.Bucket(garbageContainersBucketName) + if garbageContainersBKT != nil { + c := garbageContainersBKT.Cursor() + for k, _ := c.First(); k != nil; k, _ = c.Next() { + if len(k) != cid.Size { // skip malformed key + db.log.Warn("skip malformed key in garbage containers bucket while migration", + zap.Int("key length", len(k))) + continue + } + var cID cid.ID + if err := cID.Decode(k); err != nil { + db.log.Warn("skip malformed container ID in garbage containers bucket while migration", + zap.Int("key length", len(k)), zap.Error(err)) + continue + } + metaBkt, err := tx.CreateBucketIfNotExists(metaBucketKey(cID)) + if err != nil { + return fmt.Errorf("create meta bucket for GC-marked container %s: %w", cID, err) + } + if err := metaBkt.Put(containerGCMarkKey, nil); err != nil { + return fmt.Errorf("write GC container marker for %s: %w", cID, err) + } + } + + err := tx.DeleteBucket(garbageContainersBucketName) + if err != nil { + return fmt.Errorf("deleting garbage containers bucket: %w", err) + } + } + currEpoch := db.epochState.CurrentEpoch() phyPrefix := mkFilterPhysicalPrefix() infoBkt := tx.Bucket(containerVolumeBucketName) diff --git a/pkg/local_object_storage/metabase/version_test.go b/pkg/local_object_storage/metabase/version_test.go index 7f9b0ebd5a..1408013b62 100644 --- a/pkg/local_object_storage/metabase/version_test.go +++ b/pkg/local_object_storage/metabase/version_test.go @@ -862,8 +862,24 @@ func TestMigrate7to8(t *testing.T) { require.NoError(t, err) } + inhumeCnr := cidtest.ID() + const inhumeObjsNum = 5 + for range inhumeObjsNum { + err := db.boltDB.Update(func(tx *bbolt.Tx) error { + o := objecttest.Object() + o.SetContainerID(inhumeCnr) + + require.NoError(t, db.updateCounter(tx, phy, 1, true)) + + return PutMetadataForObject(tx, o, true) + }) + require.NoError(t, err) + } + _, err := db.InhumeContainer(inhumeCnr) + require.NoError(t, err) + // one more parent (virtual) object - err := db.boltDB.Update(func(tx *bbolt.Tx) error { + err = db.boltDB.Update(func(tx *bbolt.Tx) error { return PutMetadataForObject(tx, objecttest.Object(), false) }) require.NoError(t, err) @@ -881,7 +897,7 @@ func TestMigrate7to8(t *testing.T) { // corrupt counters b := tx.Bucket(shardInfoBucket) require.NotNil(t, b) - require.Equal(t, uint64(objsNum), binary.LittleEndian.Uint64(b.Get(objectPhyCounterKey))) + require.Equal(t, uint64(objsNum+inhumeObjsNum), binary.LittleEndian.Uint64(b.Get(objectPhyCounterKey))) require.Equal(t, uint64(objsNum), binary.LittleEndian.Uint64(b.Get(objectLogicCounterKey))) cc := make([]byte, 8) binary.LittleEndian.PutUint64(cc, uint64(100)) // corrupt physical counter @@ -917,15 +933,27 @@ func TestMigrate7to8(t *testing.T) { objsNumRead := binary.LittleEndian.Uint64(objsNumRaw) require.Equal(t, uint64(objsNum), objsNumRead) + // verify GC meta marker present + metaBkt := tx.Bucket(metaBucketKey(inhumeCnr)) + require.NotNil(t, metaBkt) + require.NotNil(t, metaBkt.Get([]byte{4})) + // check new version bkt := tx.Bucket([]byte{0x05}) require.NotNil(t, bkt) require.Equal(t, []byte{currentMetaVersion, 0, 0, 0, 0, 0, 0, 0}, bkt.Get([]byte("version"))) pc, lc := getCounters(tx) - require.Equal(t, uint64(objsNum), pc) + require.Equal(t, uint64(objsNum+inhumeObjsNum), pc) require.Equal(t, uint64(objsNum), lc) return nil }) require.NoError(t, err) + + // Verify GetGarbage sees the container via meta marker (should list all objects and list the container) + gObjs, gCnrs, err := db.GetGarbage(inhumeObjsNum + 5) + require.NoError(t, err) + require.Len(t, gObjs, inhumeObjsNum) + require.Len(t, gCnrs, 1) + require.Equal(t, inhumeCnr, gCnrs[0]) }