Skip to content

Commit bb155f3

Browse files
authored
metabase: use meta bucket to mark containers with GC (#3561)
Closes #3521.
2 parents 2d3cdb5 + 8f0905b commit bb155f3

File tree

17 files changed

+158
-114
lines changed

17 files changed

+158
-114
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ Changelog for NeoFS Node
1717
- `neofs-cli object range` command now creates file with `rw-r--r--` permissions (#3544)
1818
- Alphabet nodes send basic storage income based on the new Reports API from `container` contract (#3053)
1919
- Use stream API of FSTree for object service `Get` operation (#3466)
20+
- Use meta buckets to mark containers with GC (#3561)
2021

2122
### Removed
2223
- `neofs-cli object head --main-only` no-op flag (#3509)

pkg/local_object_storage/metabase/VERSION.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,6 @@ The lowest not used bucket index: 20.
1616
- Name: `1`
1717
- Key: object address
1818
- Value: dummy value
19-
- Garbage containers bucket
20-
- Name: `17`
21-
- Key: container ID
22-
- Value: dummy value
2319
- Bucket containing IDs of objects that are candidates for moving
2420
to another shard.
2521
- Name: `2`
@@ -51,6 +47,8 @@ The lowest not used bucket index: 20.
5147
Sign byte is 0 for negatives, 1 otherwise. Bits are inverted for negatives also.
5248
- `2` + attribute + `0x00` + value + `0x00` + object ID
5349
- `3` + object ID + attribute + `0x00` + value
50+
- `4` — container-level GC mark. \
51+
Presence means the whole container is scheduled for garbage collection.
5452

5553
# History
5654

@@ -59,6 +57,8 @@ The lowest not used bucket index: 20.
5957
Container statistic is now a bucket with multiple values. In the version number
6058
of objects was added
6159

60+
Drop garbage containers index (17), replaced with mark in metadata bucket.
61+
6262
## Version 7
6363

6464
Fixed version 6 which could store OID keys in garbage object bucket.

pkg/local_object_storage/metabase/containers.go

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -205,14 +205,6 @@ func (db *DB) DeleteContainer(cID cid.ID) error {
205205
return fmt.Errorf("metadata bucket cleanup: %w", err)
206206
}
207207

208-
cnrGCBkt := tx.Bucket(garbageContainersBucketName)
209-
if cnrGCBkt != nil {
210-
err = cnrGCBkt.Delete(cIDRaw)
211-
if err != nil {
212-
return fmt.Errorf("garbage containers cleanup: %w", err)
213-
}
214-
}
215-
216208
return nil
217209
})
218210
}

pkg/local_object_storage/metabase/control.go

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -86,13 +86,12 @@ func (db *DB) init(reset bool) error {
8686
}
8787

8888
mStaticBuckets := map[string]struct{}{
89-
string(containerVolumeBucketName): {},
90-
string(graveyardBucketName): {},
91-
string(toMoveItBucketName): {},
92-
string(garbageObjectsBucketName): {},
93-
string(garbageContainersBucketName): {},
94-
string(shardInfoBucket): {},
95-
string(bucketNameLocked): {},
89+
string(containerVolumeBucketName): {},
90+
string(graveyardBucketName): {},
91+
string(toMoveItBucketName): {},
92+
string(garbageObjectsBucketName): {},
93+
string(shardInfoBucket): {},
94+
string(bucketNameLocked): {},
9695
}
9796

9897
if !reset {

pkg/local_object_storage/metabase/counter.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,6 @@ func syncCounter(tx *bbolt.Tx, currEpoch uint64, force bool) error {
140140

141141
graveyardBKT := tx.Bucket(graveyardBucketName)
142142
garbageObjectsBKT := tx.Bucket(garbageObjectsBucketName)
143-
garbageContainersBKT := tx.Bucket(garbageContainersBucketName)
144143
key := make([]byte, addressKeySize)
145144

146145
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 {
157156

158157
// check if an object is available: not with GCMark
159158
// and not covered with a tombstone
160-
if inGraveyardWithKey(metaCursor, addressKey(addr, key), graveyardBKT, garbageObjectsBKT, garbageContainersBKT) == statusAvailable {
159+
if inGraveyardWithKey(metaCursor, addressKey(addr, key), graveyardBKT, garbageObjectsBKT) == statusAvailable {
161160
logicCounter++
162161
}
163162

pkg/local_object_storage/metabase/delete.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -132,15 +132,14 @@ func (db *DB) delete(tx *bbolt.Tx, addr oid.Address, currEpoch uint64) (bool, bo
132132
cID := addr.Container()
133133
addrKey := addressKey(addr, key)
134134
garbageObjectsBKT := tx.Bucket(garbageObjectsBucketName)
135-
garbageContainersBKT := tx.Bucket(garbageContainersBucketName)
136135
graveyardBKT := tx.Bucket(graveyardBucketName)
137136
metaBucket := tx.Bucket(metaBucketKey(cID))
138137
var metaCursor *bbolt.Cursor
139138
if metaBucket != nil {
140139
metaCursor = metaBucket.Cursor()
141140
}
142141

143-
removeAvailableObject := inGraveyardWithKey(metaCursor, addrKey, graveyardBKT, garbageObjectsBKT, garbageContainersBKT) == statusAvailable
142+
removeAvailableObject := inGraveyardWithKey(metaCursor, addrKey, graveyardBKT, garbageObjectsBKT) == statusAvailable
144143

145144
// remove record from the garbage bucket
146145
if garbageObjectsBKT != nil {

pkg/local_object_storage/metabase/exists.go

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -145,15 +145,18 @@ func objectStatus(tx *bbolt.Tx, metaCursor *bbolt.Cursor, addr oid.Address, curr
145145
// where a single address needs to be checked.
146146
func inGraveyard(tx *bbolt.Tx, metaCursor *bbolt.Cursor, addr oid.Address) uint8 {
147147
var (
148-
addrKey = addressKey(addr, make([]byte, addressKeySize))
149-
garbageContainersBkt = tx.Bucket(garbageContainersBucketName)
150-
garbageObjectsBkt = tx.Bucket(garbageObjectsBucketName)
151-
graveyardBkt = tx.Bucket(graveyardBucketName)
148+
addrKey = addressKey(addr, make([]byte, addressKeySize))
149+
garbageObjectsBkt = tx.Bucket(garbageObjectsBucketName)
150+
graveyardBkt = tx.Bucket(graveyardBucketName)
152151
)
153-
return inGraveyardWithKey(metaCursor, addrKey, graveyardBkt, garbageObjectsBkt, garbageContainersBkt)
152+
return inGraveyardWithKey(metaCursor, addrKey, graveyardBkt, garbageObjectsBkt)
154153
}
155154

156-
func inGraveyardWithKey(metaCursor *bbolt.Cursor, addrKey []byte, graveyard, garbageObjectsBCK, garbageContainersBCK *bbolt.Bucket) uint8 {
155+
func inGraveyardWithKey(metaCursor *bbolt.Cursor, addrKey []byte, graveyard, garbageObjectsBCK *bbolt.Bucket) uint8 {
156+
if metaCursor != nil && containerMarkedGC(metaCursor) {
157+
return statusGCMarked
158+
}
159+
157160
if associatedWithTypedObject(0, metaCursor, oid.ID(addrKey[cid.Size:]), objectSDK.TypeTombstone) {
158161
return statusTombstoned
159162
}
@@ -171,11 +174,7 @@ func inGraveyardWithKey(metaCursor *bbolt.Cursor, addrKey []byte, graveyard, gar
171174
return statusAvailable
172175
}
173176

174-
val = garbageContainersBCK.Get(addrKey[:cidSize])
175-
if val == nil {
176-
val = garbageObjectsBCK.Get(addrKey)
177-
}
178-
177+
val = garbageObjectsBCK.Get(addrKey)
179178
if val != nil {
180179
// object has been marked with GC
181180
return statusGCMarked

pkg/local_object_storage/metabase/graveyard.go

Lines changed: 21 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -288,7 +288,6 @@ func (db *DB) GetGarbage(limit int) ([]oid.Address, []cid.ID, error) {
288288
initCap := min(limit, reasonableLimit)
289289

290290
var addrBuff oid.Address
291-
var cidBuff cid.ID
292291
alreadyHandledContainers := make(map[cid.ID]struct{})
293292
resObjects := make([]oid.Address, 0, initCap)
294293
resContainers := make([]cid.ID, 0)
@@ -299,26 +298,34 @@ func (db *DB) GetGarbage(limit int) ([]oid.Address, []cid.ID, error) {
299298
// also be deleted as a part of non-existing
300299
// container so no need to handle it twice
301300

302-
bkt := tx.Bucket(garbageContainersBucketName)
303-
c := bkt.Cursor()
304-
305-
for k, _ := c.First(); k != nil; k, _ = c.Next() {
306-
err := cidBuff.Decode(k)
307-
if err != nil {
308-
return fmt.Errorf("parsing raw CID: %w", err)
301+
var inhumedCnrs []cid.ID
302+
err := tx.ForEach(func(name []byte, b *bbolt.Bucket) error {
303+
if name[0] == metadataPrefix && containerMarkedGC(b.Cursor()) {
304+
var cnr cid.ID
305+
cidRaw, prefix := parseContainerIDWithPrefix(&cnr, name)
306+
if cidRaw == nil || prefix != metadataPrefix {
307+
return nil
308+
}
309+
inhumedCnrs = append(inhumedCnrs, cnr)
309310
}
311+
return nil
312+
})
313+
if err != nil {
314+
return fmt.Errorf("scanning inhumed containers: %w", err)
315+
}
310316

311-
resObjects, err = listContainerObjects(tx, cidBuff, resObjects, limit)
317+
for _, cnr := range inhumedCnrs {
318+
resObjects, err = listContainerObjects(tx, cnr, resObjects, limit)
312319
if err != nil {
313-
return fmt.Errorf("listing objects for %s container: %w", cidBuff, err)
320+
return fmt.Errorf("listing objects for %s container: %w", cnr, err)
314321
}
315322

316-
alreadyHandledContainers[cidBuff] = struct{}{}
323+
alreadyHandledContainers[cnr] = struct{}{}
317324

318325
if len(resObjects) < limit {
319326
// all the objects from the container were listed,
320327
// container can be removed
321-
resContainers = append(resContainers, cidBuff)
328+
resContainers = append(resContainers, cnr)
322329
} else {
323330
return nil
324331
}
@@ -327,8 +334,8 @@ func (db *DB) GetGarbage(limit int) ([]oid.Address, []cid.ID, error) {
327334
// deleted containers are not enough to reach the limit,
328335
// check manually deleted objects then
329336

330-
bkt = tx.Bucket(garbageObjectsBucketName)
331-
c = bkt.Cursor()
337+
bkt := tx.Bucket(garbageObjectsBucketName)
338+
c := bkt.Cursor()
332339

333340
for k, _ := c.First(); k != nil; k, _ = c.Next() {
334341
err := decodeAddressFromKey(&addrBuff, k)

pkg/local_object_storage/metabase/inhume.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,6 @@ func (db *DB) inhume(tombstone *oid.Address, tombExpiration uint64, force bool,
5858
)
5959
err = db.boltDB.Update(func(tx *bbolt.Tx) error {
6060
garbageObjectsBKT := tx.Bucket(garbageObjectsBucketName)
61-
garbageContainersBKT := tx.Bucket(garbageContainersBucketName)
6261
graveyardBKT := tx.Bucket(graveyardBucketName)
6362

6463
var (
@@ -125,7 +124,7 @@ func (db *DB) inhume(tombstone *oid.Address, tombExpiration uint64, force bool,
125124
obj, err := get(tx, addr, false, true, currEpoch)
126125
targetKey := addressKey(addr, buf)
127126
if err == nil {
128-
if inGraveyardWithKey(metaCursor, targetKey, graveyardBKT, garbageObjectsBKT, garbageContainersBKT) == statusAvailable {
127+
if inGraveyardWithKey(metaCursor, targetKey, graveyardBKT, garbageObjectsBKT) == statusAvailable {
129128
// object is available, decrement the
130129
// logical counter
131130
inhumed++
@@ -219,13 +218,14 @@ func (db *DB) InhumeContainer(cID cid.ID) (uint64, error) {
219218
}
220219

221220
var removedAvailable uint64
222-
rawCID := cID[:]
223221

224222
err := db.boltDB.Update(func(tx *bbolt.Tx) error {
225-
garbageContainersBKT := tx.Bucket(garbageContainersBucketName)
226-
err := garbageContainersBKT.Put(rawCID, zeroValue)
223+
metaBkt, err := tx.CreateBucketIfNotExists(metaBucketKey(cID))
227224
if err != nil {
228-
return fmt.Errorf("put GC mark for container: %w", err)
225+
return fmt.Errorf("create meta bucket: %w", err)
226+
}
227+
if err := metaBkt.Put(containerGCMarkKey, nil); err != nil {
228+
return fmt.Errorf("write container GC mark: %w", err)
229229
}
230230

231231
info := db.containerInfo(tx, cID)

pkg/local_object_storage/metabase/list.go

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,6 @@ func (db *DB) listWithCursor(tx *bbolt.Tx, currEpoch uint64, result []objectcore
6464
var offset []byte
6565
graveyardBkt := tx.Bucket(graveyardBucketName)
6666
garbageObjectsBkt := tx.Bucket(garbageObjectsBucketName)
67-
garbageContainersBkt := tx.Bucket(garbageContainersBucketName)
6867

6968
var rawAddr = make([]byte, cidSize, addressKeySize)
7069

@@ -78,7 +77,7 @@ loop:
7877
bkt := tx.Bucket(name)
7978
if bkt != nil {
8079
copy(rawAddr, cidRaw)
81-
result, offset, cursor = selectNFromBucket(bkt, currEpoch, graveyardBkt, garbageObjectsBkt, garbageContainersBkt, rawAddr, containerID,
80+
result, offset, cursor = selectNFromBucket(bkt, currEpoch, graveyardBkt, garbageObjectsBkt, rawAddr, containerID,
8281
result, count, cursor, threshold)
8382
}
8483
bucketName = name
@@ -112,7 +111,7 @@ loop:
112111
// object to start selecting from. Ignores inhumed objects.
113112
func selectNFromBucket(bkt *bbolt.Bucket, // main bucket
114113
currEpoch uint64,
115-
graveyardBkt, garbageObjectsBkt, garbageContainersBkt *bbolt.Bucket, // cached graveyard buckets
114+
graveyardBkt, garbageObjectsBkt *bbolt.Bucket, // cached graveyard buckets
116115
cidRaw []byte, // container ID prefix, optimization
117116
cnt cid.ID, // container ID
118117
to []objectcore.AddressWithType, // listing result
@@ -132,6 +131,10 @@ func selectNFromBucket(bkt *bbolt.Bucket, // main bucket
132131
typePrefix = make([]byte, metaIDTypePrefixSize)
133132
)
134133

134+
if containerMarkedGC(c) {
135+
return to, nil, cursor
136+
}
137+
135138
fillIDTypePrefix(typePrefix)
136139

137140
if threshold {
@@ -157,7 +160,7 @@ func selectNFromBucket(bkt *bbolt.Bucket, // main bucket
157160

158161
mCursor := bkt.Cursor()
159162
offset = k
160-
if inGraveyardWithKey(mCursor, append(cidRaw, obj[:]...), graveyardBkt, garbageObjectsBkt, garbageContainersBkt) != statusAvailable {
163+
if inGraveyardWithKey(mCursor, append(cidRaw, obj[:]...), graveyardBkt, garbageObjectsBkt) != statusAvailable {
161164
continue
162165
}
163166

0 commit comments

Comments
 (0)