Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

neofs-lens/storage: add a sanity storage check #2884

Merged
merged 1 commit into from
Jul 10, 2024
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ Changelog for NeoFS Node

### Added
- Indexes inspection command to neofs-lens (#2882)
- Add objects sanity checker to neofs-lens (#2506)

### Fixed
- Control service's Drop call does not clean metabase (#2822)
Expand Down
1 change: 1 addition & 0 deletions cmd/neofs-lens/internal/storage/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
storageGetObjCMD,
storageListObjsCMD,
storageStatusObjCMD,
storageSanityCMD,

Check warning on line 48 in cmd/neofs-lens/internal/storage/root.go

View check run for this annotation

Codecov / codecov/patch

cmd/neofs-lens/internal/storage/root.go#L48

Added line #L48 was not covered by tests
)
}

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

import (
"bytes"
"context"
"errors"
"fmt"
"time"

"github.com/mr-tron/base58"
common "github.com/nspcc-dev/neofs-node/cmd/neofs-lens/internal"
"github.com/nspcc-dev/neofs-node/cmd/neofs-node/config"
engineconfig "github.com/nspcc-dev/neofs-node/cmd/neofs-node/config/engine"
shardconfig "github.com/nspcc-dev/neofs-node/cmd/neofs-node/config/engine/shard"
fstreeconfig "github.com/nspcc-dev/neofs-node/cmd/neofs-node/config/engine/shard/blobstor/fstree"
peapodconfig "github.com/nspcc-dev/neofs-node/cmd/neofs-node/config/engine/shard/blobstor/peapod"
objectcore "github.com/nspcc-dev/neofs-node/pkg/core/object"
commonb "github.com/nspcc-dev/neofs-node/pkg/local_object_storage/blobstor/common"
"github.com/nspcc-dev/neofs-node/pkg/local_object_storage/blobstor/compression"
"github.com/nspcc-dev/neofs-node/pkg/local_object_storage/blobstor/fstree"
"github.com/nspcc-dev/neofs-node/pkg/local_object_storage/blobstor/peapod"
meta "github.com/nspcc-dev/neofs-node/pkg/local_object_storage/metabase"
"github.com/nspcc-dev/neofs-node/pkg/local_object_storage/shard/mode"
"github.com/nspcc-dev/neofs-node/pkg/local_object_storage/util/logicerr"
"github.com/nspcc-dev/neofs-sdk-go/object"
"github.com/spf13/cobra"
"go.etcd.io/bbolt"
"go.uber.org/zap"
)

var storageSanityCMD = &cobra.Command{
Use: "sanity",
Short: "Check consistency of stored objects",
Args: cobra.NoArgs,
Run: sanityCheck,
}

func init() {
common.AddConfigFileFlag(storageSanityCMD, &vConfig)

Check warning on line 39 in cmd/neofs-lens/internal/storage/sanity.go

View check run for this annotation

Codecov / codecov/patch

cmd/neofs-lens/internal/storage/sanity.go#L38-L39

Added lines #L38 - L39 were not covered by tests
}

type storageShard struct {
m *meta.DB
fsT *fstree.FSTree
p *peapod.Peapod
}

func sanityCheck(cmd *cobra.Command, _ []string) {
var shards []storageShard
defer func() {
for _, sh := range shards {
_ = sh.m.Close()
_ = sh.p.Close()
_ = sh.fsT.Close()

Check warning on line 54 in cmd/neofs-lens/internal/storage/sanity.go

View check run for this annotation

Codecov / codecov/patch

cmd/neofs-lens/internal/storage/sanity.go#L48-L54

Added lines #L48 - L54 were not covered by tests
}
}()

appCfg := config.New(config.Prm{}, config.WithConfigFile(vConfig))
err := engineconfig.IterateShards(appCfg, false, func(sc *shardconfig.Config) error {
var sh storageShard

Check warning on line 60 in cmd/neofs-lens/internal/storage/sanity.go

View check run for this annotation

Codecov / codecov/patch

cmd/neofs-lens/internal/storage/sanity.go#L58-L60

Added lines #L58 - L60 were not covered by tests

blobStorCfg := sc.BlobStor()
metaCfg := sc.Metabase()

Check warning on line 63 in cmd/neofs-lens/internal/storage/sanity.go

View check run for this annotation

Codecov / codecov/patch

cmd/neofs-lens/internal/storage/sanity.go#L62-L63

Added lines #L62 - L63 were not covered by tests

sh.m = meta.New(
meta.WithPath(metaCfg.Path()),
meta.WithPermissions(metaCfg.BoltDB().Perm()),
meta.WithMaxBatchSize(metaCfg.BoltDB().MaxBatchSize()),
meta.WithMaxBatchDelay(metaCfg.BoltDB().MaxBatchDelay()),
meta.WithBoltDBOptions(&bbolt.Options{Timeout: time.Second}),
meta.WithLogger(zap.NewNop()),
cthulhu-rider marked this conversation as resolved.
Show resolved Hide resolved
meta.WithEpochState(epochState{}),
)

Check warning on line 73 in cmd/neofs-lens/internal/storage/sanity.go

View check run for this annotation

Codecov / codecov/patch

cmd/neofs-lens/internal/storage/sanity.go#L65-L73

Added lines #L65 - L73 were not covered by tests

for _, subCfg := range blobStorCfg.Storages() {
switch subCfg.Type() {
default:
return fmt.Errorf("unsupported sub-storage type '%s'", subCfg.Type())
case peapod.Type:
peapodCfg := peapodconfig.From((*config.Config)(subCfg))
sh.p = peapod.New(subCfg.Path(), subCfg.Perm(), peapodCfg.FlushInterval())

Check warning on line 81 in cmd/neofs-lens/internal/storage/sanity.go

View check run for this annotation

Codecov / codecov/patch

cmd/neofs-lens/internal/storage/sanity.go#L75-L81

Added lines #L75 - L81 were not covered by tests

var compressCfg compression.Config
err := compressCfg.Init()
common.ExitOnErr(cmd, common.Errf("failed to init compression config: %w", err))

Check warning on line 85 in cmd/neofs-lens/internal/storage/sanity.go

View check run for this annotation

Codecov / codecov/patch

cmd/neofs-lens/internal/storage/sanity.go#L83-L85

Added lines #L83 - L85 were not covered by tests

sh.p.SetCompressor(&compressCfg)
case fstree.Type:
fstreeCfg := fstreeconfig.From((*config.Config)(subCfg))
sh.fsT = fstree.New(
fstree.WithPath(subCfg.Path()),
fstree.WithPerm(subCfg.Perm()),
fstree.WithDepth(fstreeCfg.Depth()),
fstree.WithNoSync(fstreeCfg.NoSync()),
)

Check warning on line 95 in cmd/neofs-lens/internal/storage/sanity.go

View check run for this annotation

Codecov / codecov/patch

cmd/neofs-lens/internal/storage/sanity.go#L87-L95

Added lines #L87 - L95 were not covered by tests
}
}

common.ExitOnErr(cmd, common.Errf("open metabase: %w", sh.m.Open(true)))
common.ExitOnErr(cmd, common.Errf("open peapod: %w", sh.p.Open(true)))
common.ExitOnErr(cmd, common.Errf("open fstree: %w", sh.fsT.Open(true)))

Check warning on line 101 in cmd/neofs-lens/internal/storage/sanity.go

View check run for this annotation

Codecov / codecov/patch

cmd/neofs-lens/internal/storage/sanity.go#L99-L101

Added lines #L99 - L101 were not covered by tests
cthulhu-rider marked this conversation as resolved.
Show resolved Hide resolved

// metabase.Open(true) does not set it mode to RO somehow
cthulhu-rider marked this conversation as resolved.
Show resolved Hide resolved
common.ExitOnErr(cmd, common.Errf("moving metabase in readonly mode", sh.m.SetMode(mode.ReadOnly)))

Check warning on line 104 in cmd/neofs-lens/internal/storage/sanity.go

View check run for this annotation

Codecov / codecov/patch

cmd/neofs-lens/internal/storage/sanity.go#L104

Added line #L104 was not covered by tests

common.ExitOnErr(cmd, common.Errf("init metabase: %w", sh.m.Init()))
common.ExitOnErr(cmd, common.Errf("init peapod: %w", sh.p.Init()))
common.ExitOnErr(cmd, common.Errf("init fstree: %w", sh.fsT.Init()))

Check warning on line 108 in cmd/neofs-lens/internal/storage/sanity.go

View check run for this annotation

Codecov / codecov/patch

cmd/neofs-lens/internal/storage/sanity.go#L106-L108

Added lines #L106 - L108 were not covered by tests

shards = append(shards, sh)

Check warning on line 110 in cmd/neofs-lens/internal/storage/sanity.go

View check run for this annotation

Codecov / codecov/patch

cmd/neofs-lens/internal/storage/sanity.go#L110

Added line #L110 was not covered by tests

return nil

Check warning on line 112 in cmd/neofs-lens/internal/storage/sanity.go

View check run for this annotation

Codecov / codecov/patch

cmd/neofs-lens/internal/storage/sanity.go#L112

Added line #L112 was not covered by tests
})
common.ExitOnErr(cmd, common.Errf("reading config: %w", err))

Check warning on line 114 in cmd/neofs-lens/internal/storage/sanity.go

View check run for this annotation

Codecov / codecov/patch

cmd/neofs-lens/internal/storage/sanity.go#L114

Added line #L114 was not covered by tests

for _, sh := range shards {
idRaw, err := sh.m.ReadShardID()
if err != nil {
cmd.Printf("reading shard id: %s; skip this shard\n", err)
continue

Check warning on line 120 in cmd/neofs-lens/internal/storage/sanity.go

View check run for this annotation

Codecov / codecov/patch

cmd/neofs-lens/internal/storage/sanity.go#L116-L120

Added lines #L116 - L120 were not covered by tests
}

id := base58.Encode(idRaw)
cmd.Printf("Checking %s shard\n", id)

Check warning on line 124 in cmd/neofs-lens/internal/storage/sanity.go

View check run for this annotation

Codecov / codecov/patch

cmd/neofs-lens/internal/storage/sanity.go#L123-L124

Added lines #L123 - L124 were not covered by tests

objsChecked, err := checkShard(cmd, sh)
if err != nil {
if errors.Is(err, context.Canceled) {
return

Check warning on line 129 in cmd/neofs-lens/internal/storage/sanity.go

View check run for this annotation

Codecov / codecov/patch

cmd/neofs-lens/internal/storage/sanity.go#L126-L129

Added lines #L126 - L129 were not covered by tests
}

cmd.Printf("%d objects checked in %s shard, interrupted by error: %s\n", objsChecked, id, err)
continue

Check warning on line 133 in cmd/neofs-lens/internal/storage/sanity.go

View check run for this annotation

Codecov / codecov/patch

cmd/neofs-lens/internal/storage/sanity.go#L132-L133

Added lines #L132 - L133 were not covered by tests
}

cmd.Printf("Checked objects in %s shard: %d", id, objsChecked)

Check warning on line 136 in cmd/neofs-lens/internal/storage/sanity.go

View check run for this annotation

Codecov / codecov/patch

cmd/neofs-lens/internal/storage/sanity.go#L136

Added line #L136 was not covered by tests
}
}

func checkShard(cmd *cobra.Command, sh storageShard) (int, error) {
var objectsChecked int
cthulhu-rider marked this conversation as resolved.
Show resolved Hide resolved
var mPrm meta.ListPrm
mPrm.SetCount(1024)

Check warning on line 143 in cmd/neofs-lens/internal/storage/sanity.go

View check run for this annotation

Codecov / codecov/patch

cmd/neofs-lens/internal/storage/sanity.go#L140-L143

Added lines #L140 - L143 were not covered by tests

for {
listRes, err := sh.m.ListWithCursor(mPrm)
if err != nil {
if errors.Is(err, meta.ErrEndOfListing) {
return objectsChecked, nil

Check warning on line 149 in cmd/neofs-lens/internal/storage/sanity.go

View check run for this annotation

Codecov / codecov/patch

cmd/neofs-lens/internal/storage/sanity.go#L145-L149

Added lines #L145 - L149 were not covered by tests
}

return objectsChecked, fmt.Errorf("listing objects in metabase: %w", err)

Check warning on line 152 in cmd/neofs-lens/internal/storage/sanity.go

View check run for this annotation

Codecov / codecov/patch

cmd/neofs-lens/internal/storage/sanity.go#L152

Added line #L152 was not covered by tests
}

for _, obj := range listRes.AddressList() {
select {
case <-cmd.Context().Done():
return objectsChecked, cmd.Context().Err()
default:

Check warning on line 159 in cmd/neofs-lens/internal/storage/sanity.go

View check run for this annotation

Codecov / codecov/patch

cmd/neofs-lens/internal/storage/sanity.go#L155-L159

Added lines #L155 - L159 were not covered by tests
}

addr := obj.Address

Check warning on line 162 in cmd/neofs-lens/internal/storage/sanity.go

View check run for this annotation

Codecov / codecov/patch

cmd/neofs-lens/internal/storage/sanity.go#L162

Added line #L162 was not covered by tests

var sIDPrm meta.StorageIDPrm
sIDPrm.SetAddress(addr)

Check warning on line 165 in cmd/neofs-lens/internal/storage/sanity.go

View check run for this annotation

Codecov / codecov/patch

cmd/neofs-lens/internal/storage/sanity.go#L164-L165

Added lines #L164 - L165 were not covered by tests

sIDRes, err := sh.m.StorageID(sIDPrm)
if err != nil {
return objectsChecked, fmt.Errorf("reading %s storage ID in metabase: %w", addr, err)

Check warning on line 169 in cmd/neofs-lens/internal/storage/sanity.go

View check run for this annotation

Codecov / codecov/patch

cmd/neofs-lens/internal/storage/sanity.go#L167-L169

Added lines #L167 - L169 were not covered by tests
}

var mGet meta.GetPrm
mGet.SetAddress(addr)

Check warning on line 173 in cmd/neofs-lens/internal/storage/sanity.go

View check run for this annotation

Codecov / codecov/patch

cmd/neofs-lens/internal/storage/sanity.go#L172-L173

Added lines #L172 - L173 were not covered by tests

getRes, err := sh.m.Get(mGet)
if err != nil {
return objectsChecked, fmt.Errorf("reading %s object in metabase: %w", addr, err)

Check warning on line 177 in cmd/neofs-lens/internal/storage/sanity.go

View check run for this annotation

Codecov / codecov/patch

cmd/neofs-lens/internal/storage/sanity.go#L175-L177

Added lines #L175 - L177 were not covered by tests
}

header := *getRes.Header()

Check warning on line 180 in cmd/neofs-lens/internal/storage/sanity.go

View check run for this annotation

Codecov / codecov/patch

cmd/neofs-lens/internal/storage/sanity.go#L180

Added line #L180 was not covered by tests

switch id := string(sIDRes.StorageID()); id {
case "":
err = checkObject(header, sh.fsT)
case peapod.Type:
err = checkObject(header, sh.p)
default:
err = fmt.Errorf("uknown storage ID: %s", id)

Check warning on line 188 in cmd/neofs-lens/internal/storage/sanity.go

View check run for this annotation

Codecov / codecov/patch

cmd/neofs-lens/internal/storage/sanity.go#L182-L188

Added lines #L182 - L188 were not covered by tests
}

if err != nil {
if errors.Is(err, logicerr.Error) {
cmd.Printf("%s object failed check: %s\n", addr, err)
continue

Check warning on line 194 in cmd/neofs-lens/internal/storage/sanity.go

View check run for this annotation

Codecov / codecov/patch

cmd/neofs-lens/internal/storage/sanity.go#L191-L194

Added lines #L191 - L194 were not covered by tests
}

return objectsChecked, fmt.Errorf("critical error at %s object check: %w", addr, err)

Check warning on line 197 in cmd/neofs-lens/internal/storage/sanity.go

View check run for this annotation

Codecov / codecov/patch

cmd/neofs-lens/internal/storage/sanity.go#L197

Added line #L197 was not covered by tests
}

objectsChecked++

Check warning on line 200 in cmd/neofs-lens/internal/storage/sanity.go

View check run for this annotation

Codecov / codecov/patch

cmd/neofs-lens/internal/storage/sanity.go#L200

Added line #L200 was not covered by tests
}

mPrm.SetCursor(listRes.Cursor())

Check warning on line 203 in cmd/neofs-lens/internal/storage/sanity.go

View check run for this annotation

Codecov / codecov/patch

cmd/neofs-lens/internal/storage/sanity.go#L203

Added line #L203 was not covered by tests
}
}

func checkObject(objHeader object.Object, storage commonb.Storage) error {

Check warning on line 207 in cmd/neofs-lens/internal/storage/sanity.go

View check run for this annotation

Codecov / codecov/patch

cmd/neofs-lens/internal/storage/sanity.go#L207

Added line #L207 was not covered by tests
// header len check

raw, err := objHeader.Marshal()
if err != nil {
return fmt.Errorf("object from metabase cannot be marshaled: %w", err)

Check warning on line 212 in cmd/neofs-lens/internal/storage/sanity.go

View check run for this annotation

Codecov / codecov/patch

cmd/neofs-lens/internal/storage/sanity.go#L210-L212

Added lines #L210 - L212 were not covered by tests
}

if lenRead := len(raw); lenRead > object.MaxHeaderLen {
return fmt.Errorf("header cannot be larger than %d bytes, read %d", object.MaxHeaderLen, lenRead)

Check warning on line 216 in cmd/neofs-lens/internal/storage/sanity.go

View check run for this annotation

Codecov / codecov/patch

cmd/neofs-lens/internal/storage/sanity.go#L215-L216

Added lines #L215 - L216 were not covered by tests
}

// object real presence

var getPrm commonb.GetPrm
getPrm.Address = objectcore.AddressOf(&objHeader)

Check warning on line 222 in cmd/neofs-lens/internal/storage/sanity.go

View check run for this annotation

Codecov / codecov/patch

cmd/neofs-lens/internal/storage/sanity.go#L221-L222

Added lines #L221 - L222 were not covered by tests

res, err := storage.Get(getPrm)
if err != nil {
return fmt.Errorf("object get from %s storage: %w", storage.Type(), err)

Check warning on line 226 in cmd/neofs-lens/internal/storage/sanity.go

View check run for this annotation

Codecov / codecov/patch

cmd/neofs-lens/internal/storage/sanity.go#L224-L226

Added lines #L224 - L226 were not covered by tests
}

storageRaw, err := res.Object.CutPayload().Marshal()
if err != nil {
return fmt.Errorf("object from %s storage cannot be marshaled: %w", storage.Type(), err)

Check warning on line 231 in cmd/neofs-lens/internal/storage/sanity.go

View check run for this annotation

Codecov / codecov/patch

cmd/neofs-lens/internal/storage/sanity.go#L229-L231

Added lines #L229 - L231 were not covered by tests
}

if !bytes.Equal(raw, storageRaw) {
return errors.New("object from metabase does not match object from storage")

Check warning on line 235 in cmd/neofs-lens/internal/storage/sanity.go

View check run for this annotation

Codecov / codecov/patch

cmd/neofs-lens/internal/storage/sanity.go#L234-L235

Added lines #L234 - L235 were not covered by tests
}

return nil

Check warning on line 238 in cmd/neofs-lens/internal/storage/sanity.go

View check run for this annotation

Codecov / codecov/patch

cmd/neofs-lens/internal/storage/sanity.go#L238

Added line #L238 was not covered by tests
}
Loading