Skip to content

Commit

Permalink
Feat/add meta indexes inspection to lens (#2882)
Browse files Browse the repository at this point in the history
  • Loading branch information
cthulhu-rider authored Jul 3, 2024
2 parents 2130a76 + c02ad89 commit 5ef73c8
Show file tree
Hide file tree
Showing 7 changed files with 172 additions and 16 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ Changelog for NeoFS Node
## [Unreleased]

### Added
- Indexes inspection command to neofs-lens (#2882)

### Fixed
- Control service's Drop call does not clean metabase (#2822)
Expand Down
1 change: 1 addition & 0 deletions cmd/neofs-lens/internal/meta/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ func init() {
listGarbageCMD,
writeObjectCMD,
getCMD,
statCMD,
)
}

Expand Down
55 changes: 55 additions & 0 deletions cmd/neofs-lens/internal/meta/status.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package meta

import (
"fmt"

common "github.com/nspcc-dev/neofs-node/cmd/neofs-lens/internal"
oid "github.com/nspcc-dev/neofs-sdk-go/object/id"
"github.com/spf13/cobra"
)

var statCMD = &cobra.Command{
Use: "status",
Short: "Object status information",
Long: `Get metabase's indexes related to an object.`,
Args: cobra.NoArgs,
Run: statusFunc,
}

func init() {
common.AddAddressFlag(statCMD, &vAddress)
common.AddComponentPathFlag(statCMD, &vPath)
}

func statusFunc(cmd *cobra.Command, _ []string) {
var addr oid.Address

err := addr.DecodeString(vAddress)
common.ExitOnErr(cmd, common.Errf("invalid address argument: %w", err))

db := openMeta(cmd, true)
defer db.Close()

res, err := db.ObjectStatus(addr)
common.ExitOnErr(cmd, common.Errf("reading object status: %w", err))

const emptyValPlaceholder = "<empty>"
storageID := res.StorageID
if storageID == "" {
storageID = emptyValPlaceholder
}

cmd.Printf("Metabase version: %d\n", res.Version)
cmd.Printf("Object state: %s\n", res.State)
cmd.Printf("Storage's ID: %s\n", storageID)
cmd.Println("Indexes:")
for _, bucket := range res.Buckets {
valStr := emptyValPlaceholder
if bucket.Value != nil {
valStr = fmt.Sprintf("%x", bucket.Value)
}

cmd.Printf("\tBucket: %d\n"+
"\tValue (HEX): %s\n", bucket.BucketIndex, valStr)
}
}
24 changes: 12 additions & 12 deletions pkg/local_object_storage/metabase/VERSION.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,55 +47,55 @@ The lowest not used bucket index: 20.
- Key: object ID
- Value: marshalled object
- Bucket containing objects of LOCK type
- Name: container ID + `7`
- Name: `7` + container ID
- Key: object ID
- Value: marshalled object
- Bucket containing objects of STORAGEGROUP type
- Name: container ID + 8
- Name: `8` + container ID
- Key: object ID
- Value: marshaled object
- Bucket containing objects of TOMBSTONE type
- Name: container ID + `9`
- Name: `9` + container ID
- Key: object ID
- Value: marshaled object
- Bucket containing object or LINK type
- Name: container ID + `18`
- Name: `18` + container ID
- Key: object ID
- Value: marshaled object
- Bucket mapping objects to the storage ID they are stored in
- Name: container ID + `10`
- Name: `10` + container ID
- Key: object ID
- Value: storage ID
- Bucket for mapping parent object to the split info
- Name: container ID + `11`
- Name: `11` + container ID
- Key: object ID
- Value: split info

### FKBT index buckets
- Bucket mapping owner to object IDs
- Name: containerID + `12`
- Name: `12` + container ID
- Key: owner ID as base58 string
- Value: bucket containing object IDs as keys
- Bucket containing objects attributes indexes
- Name: containerID + `13` + attribute key
- Name: `13` + container ID + attribute key
- Key: attribute value
- Value: bucket containing object IDs as keys

### List index buckets
- Bucket mapping payload hash to a list of object IDs
- Name: container ID + `14`
- Name: `14` + container ID
- Key: payload hash
- Value: list of object IDs
- Bucket mapping parent ID to a list of children IDs
- Name: container ID + `15`
- Name: `15` + container ID
- Key: parent ID
- Value: list of children object IDs
- Bucket mapping split ID to a list of object IDs
- Name: container ID + `16`
- Name: `16` + container ID
- Key: split ID
- Value: list of object IDs
- Bucket mapping first object ID to a list of objects IDs
- Name: container ID + `19`
- Name: `19` + container ID
- Key: first object ID
- Value: objects for corresponding split chain

Expand Down
89 changes: 85 additions & 4 deletions pkg/local_object_storage/metabase/status.go
Original file line number Diff line number Diff line change
@@ -1,21 +1,33 @@
package meta

import (
"errors"
"bytes"
"fmt"

cid "github.com/nspcc-dev/neofs-sdk-go/container/id"
oid "github.com/nspcc-dev/neofs-sdk-go/object/id"
"go.etcd.io/bbolt"
)

// BucketValue pairs a bucket index and a value that relates
// an object.
type BucketValue struct {
BucketIndex int
Value []byte
}

// ObjectStatus represents the status of the object in the Metabase.
type ObjectStatus struct {
Version uint64
Buckets []BucketValue
State []string
Path string
StorageID string
Error error
}

// ObjectStatus returns the status of the object in the Metabase. It contains state, path and storageID.
// ObjectStatus returns the status of the object in the Metabase. It contains state, path, storageID
// and indexed information about an object.
func (db *DB) ObjectStatus(address oid.Address) (ObjectStatus, error) {
db.modeMtx.RLock()
defer db.modeMtx.RUnlock()
Expand All @@ -27,17 +39,25 @@ func (db *DB) ObjectStatus(address oid.Address) (ObjectStatus, error) {
storageID := StorageIDPrm{}
storageID.SetAddress(address)
resStorageID, err := db.StorageID(storageID)
if id := resStorageID.StorageID(); id != nil {
res.Error = errors.New("unexpected storage id")
if err != nil {
res.Error = fmt.Errorf("reading storage ID: %w", err)
return res, res.Error
}

if id := resStorageID.StorageID(); id != nil {
res.StorageID = string(id)
}

err = db.boltDB.View(func(tx *bbolt.Tx) error {
res.Version = getVersion(tx)

oID := address.Object()
cID := address.Container()
objKey := objectKey(address.Object(), make([]byte, objectKeySize))
key := make([]byte, bucketKeySize)

res.Buckets = readBuckets(tx, cID, objKey)

if objectLocked(tx, cID, oID) {
res.State = append(res.State, "LOCKED")
}
Expand All @@ -64,3 +84,64 @@ func (db *DB) ObjectStatus(address oid.Address) (ObjectStatus, error) {
res.Error = err
return res, err
}

func readBuckets(tx *bbolt.Tx, cID cid.ID, objKey []byte) []BucketValue {
var res []BucketValue
cIDRaw := containerKey(cID, make([]byte, cidSize))

objectBuckets := [][]byte{
graveyardBucketName,
garbageObjectsBucketName,
toMoveItBucketName,
primaryBucketName(cID, make([]byte, bucketKeySize)),
bucketNameLockers(cID, make([]byte, bucketKeySize)),
storageGroupBucketName(cID, make([]byte, bucketKeySize)),
tombstoneBucketName(cID, make([]byte, bucketKeySize)),
smallBucketName(cID, make([]byte, bucketKeySize)),
rootBucketName(cID, make([]byte, bucketKeySize)),
parentBucketName(cID, make([]byte, bucketKeySize)),
linkObjectsBucketName(cID, make([]byte, bucketKeySize)),
firstObjectIDBucketName(cID, make([]byte, bucketKeySize)),
}

for _, bucketKey := range objectBuckets {
b := tx.Bucket(bucketKey)
if b == nil {
continue
}

res = append(res, BucketValue{
BucketIndex: int(bucketKey[0]), // the first byte is always a prefix
Value: bytes.Clone(b.Get(objKey)),
})
}

containerBuckets := []byte{
containerVolumePrefix,
garbageContainersPrefix,
}

for _, bucketKey := range containerBuckets {
b := tx.Bucket([]byte{bucketKey})
if b == nil {
continue
}

res = append(res, BucketValue{
BucketIndex: int(bucketKey),
Value: bytes.Clone(b.Get(cIDRaw)),
})
}

if b := tx.Bucket(bucketNameLocked); b != nil {
b = b.Bucket(cIDRaw)
if b != nil {
res = append(res, BucketValue{
BucketIndex: lockedPrefix,
Value: bytes.Clone(b.Get(objKey)),
})
}
}

return res
}
6 changes: 6 additions & 0 deletions pkg/local_object_storage/metabase/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,12 @@ func objectKey(obj oid.ID, key []byte) []byte {
return key[:objectKeySize]
}

// containerKey returns key for K-V tables when key is a container ID.
func containerKey(cID cid.ID, key []byte) []byte {
cID.Encode(key)
return key[:cidSize]
}

// if meets irregular object container in objs - returns its type, otherwise returns object.TypeRegular.
//
// firstIrregularObjectType(tx, cnr, obj) usage allows getting object type.
Expand Down
12 changes: 12 additions & 0 deletions pkg/local_object_storage/metabase/version.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,3 +59,15 @@ func updateVersion(tx *bbolt.Tx, version uint64) error {
}
return b.Put(versionKey, data)
}

func getVersion(tx *bbolt.Tx) uint64 {
b := tx.Bucket(shardInfoBucket)
if b != nil {
data := b.Get(versionKey)
if len(data) == 8 {
return binary.LittleEndian.Uint64(data)
}
}

return 0
}

0 comments on commit 5ef73c8

Please sign in to comment.