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

Revive dead object via control service #2968

Merged
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 @@ -14,6 +14,7 @@ attribute, which is used for container domain name in NNS contracts (#2954)
- Save last epoch when metabase was resynchronized (#2966)
- `neofs-lens meta last-resync-epoch` command (#2966)
- `neofs-lens fstree cleanup-tmp` command (#2967)
- `neofs-cli control object revive` command (#2968)

### Fixed
- Do not search for tombstones when handling their expiration, use local indexes instead (#2929)
Expand Down
4 changes: 4 additions & 0 deletions cmd/neofs-cli/modules/control/object.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
"github.com/spf13/cobra"
)

const objectFlag = "object"

var objectCmd = &cobra.Command{
Use: "object",
Short: "Direct object operations with storage engine",
Expand All @@ -12,7 +14,9 @@
func initControlObjectsCmd() {
objectCmd.AddCommand(listObjectsCmd)
objectCmd.AddCommand(objectStatusCmd)
objectCmd.AddCommand(reviveObjectCmd)

Check warning on line 17 in cmd/neofs-cli/modules/control/object.go

View check run for this annotation

Codecov / codecov/patch

cmd/neofs-cli/modules/control/object.go#L17

Added line #L17 was not covered by tests

initControlObjectReviveCmd()

Check warning on line 19 in cmd/neofs-cli/modules/control/object.go

View check run for this annotation

Codecov / codecov/patch

cmd/neofs-cli/modules/control/object.go#L19

Added line #L19 was not covered by tests
initControlObjectsListCmd()
initObjectStatusFlags()
}
82 changes: 82 additions & 0 deletions cmd/neofs-cli/modules/control/object_revive.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
package control

import (
"fmt"

rawclient "github.com/nspcc-dev/neofs-api-go/v2/rpc/client"
"github.com/nspcc-dev/neofs-node/cmd/neofs-cli/internal/commonflags"
"github.com/nspcc-dev/neofs-node/cmd/neofs-cli/internal/key"
"github.com/nspcc-dev/neofs-node/pkg/services/control"
"github.com/spf13/cobra"
)

var reviveObjectCmd = &cobra.Command{
Use: "revive",
Short: "Forcefully revive object",
Long: "Purge removal marks from metabases",
Args: cobra.NoArgs,
RunE: reviveObject,
}

func initControlObjectReviveCmd() {
initControlFlags(reviveObjectCmd)

flags := reviveObjectCmd.Flags()
flags.String(objectFlag, "", "Object address")

Check warning on line 25 in cmd/neofs-cli/modules/control/object_revive.go

View check run for this annotation

Codecov / codecov/patch

cmd/neofs-cli/modules/control/object_revive.go#L21-L25

Added lines #L21 - L25 were not covered by tests
}

func reviveObject(cmd *cobra.Command, _ []string) error {
ctx, cancel := commonflags.GetCommandContext(cmd)
defer cancel()

pk, err := key.Get(cmd)
if err != nil {
return err
}
addressRaw, err := cmd.Flags().GetString(objectFlag)
if err != nil {
return fmt.Errorf("reading %s flag: %w", objectFlag, err)
}

Check warning on line 39 in cmd/neofs-cli/modules/control/object_revive.go

View check run for this annotation

Codecov / codecov/patch

cmd/neofs-cli/modules/control/object_revive.go#L28-L39

Added lines #L28 - L39 were not covered by tests

var resp *control.ReviveObjectResponse
req := &control.ReviveObjectRequest{
Body: &control.ReviveObjectRequest_Body{
ObjectAddress: []byte(addressRaw),
},
}
err = signRequest(pk, req)
if err != nil {
return err
}

Check warning on line 50 in cmd/neofs-cli/modules/control/object_revive.go

View check run for this annotation

Codecov / codecov/patch

cmd/neofs-cli/modules/control/object_revive.go#L41-L50

Added lines #L41 - L50 were not covered by tests

cli, err := getClient(ctx)
if err != nil {
return err
}

Check warning on line 55 in cmd/neofs-cli/modules/control/object_revive.go

View check run for this annotation

Codecov / codecov/patch

cmd/neofs-cli/modules/control/object_revive.go#L52-L55

Added lines #L52 - L55 were not covered by tests

err = cli.ExecRaw(func(client *rawclient.Client) error {
resp, err = control.ReviveObject(client, req)
return err
})
if err != nil {
return fmt.Errorf("rpc error: %w", err)
}

Check warning on line 63 in cmd/neofs-cli/modules/control/object_revive.go

View check run for this annotation

Codecov / codecov/patch

cmd/neofs-cli/modules/control/object_revive.go#L57-L63

Added lines #L57 - L63 were not covered by tests

err = verifyResponse(resp.GetSignature(), resp.GetBody())
if err != nil {
return err
}

Check warning on line 68 in cmd/neofs-cli/modules/control/object_revive.go

View check run for this annotation

Codecov / codecov/patch

cmd/neofs-cli/modules/control/object_revive.go#L65-L68

Added lines #L65 - L68 were not covered by tests

shards := resp.GetBody().GetShards()
if len(shards) == 0 {
cmd.Println("<empty response>")
return nil
}

Check warning on line 74 in cmd/neofs-cli/modules/control/object_revive.go

View check run for this annotation

Codecov / codecov/patch

cmd/neofs-cli/modules/control/object_revive.go#L70-L74

Added lines #L70 - L74 were not covered by tests

for _, shard := range shards {
cmd.Printf("Shard ID: %s\n", shard.ShardId)
cmd.Printf("Revival status: %s\n", shard.Status)
}

Check warning on line 79 in cmd/neofs-cli/modules/control/object_revive.go

View check run for this annotation

Codecov / codecov/patch

cmd/neofs-cli/modules/control/object_revive.go#L76-L79

Added lines #L76 - L79 were not covered by tests

return nil

Check warning on line 81 in cmd/neofs-cli/modules/control/object_revive.go

View check run for this annotation

Codecov / codecov/patch

cmd/neofs-cli/modules/control/object_revive.go#L81

Added line #L81 was not covered by tests
}
2 changes: 0 additions & 2 deletions cmd/neofs-cli/modules/control/object_status.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,6 @@ import (
"github.com/spf13/cobra"
)

const objectFlag = "object"

var objectStatusCmd = &cobra.Command{
Use: "status",
Short: "Check current object status",
Expand Down
41 changes: 41 additions & 0 deletions pkg/local_object_storage/engine/revive.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package engine

import (
meta "github.com/nspcc-dev/neofs-node/pkg/local_object_storage/metabase"
oid "github.com/nspcc-dev/neofs-sdk-go/object/id"
"go.uber.org/zap"
)

// ReviveShardStatus contains the Status of the object's revival in the Shard and Shard ID.
type ReviveShardStatus struct {
ID string
Status meta.ReviveStatus
}

// ReviveStatus represents the status of the object's revival in the StorageEngine.
type ReviveStatus struct {
Shards []ReviveShardStatus
}

// ReviveObject forcefully revives object by oid.Address in the StorageEngine.
// Iterate over all shards despite errors and purge all removal marks from all metabases.
func (e *StorageEngine) ReviveObject(address oid.Address) (res ReviveStatus, err error) {
roman-khimov marked this conversation as resolved.
Show resolved Hide resolved
e.iterateOverUnsortedShards(func(sh hashedShard) (stop bool) {
reviveStatus, err := sh.ReviveObject(address)
id := *sh.ID()
res.Shards = append(res.Shards, ReviveShardStatus{
ID: id.String(),
Status: reviveStatus,
})
if err != nil {
e.log.Warn("failed to revive object in shard",
zap.String("shard", id.String()),
zap.String("address", address.EncodeToString()),
zap.Error(err),
)
}

Check warning on line 36 in pkg/local_object_storage/engine/revive.go

View check run for this annotation

Codecov / codecov/patch

pkg/local_object_storage/engine/revive.go#L22-L36

Added lines #L22 - L36 were not covered by tests

return false

Check warning on line 38 in pkg/local_object_storage/engine/revive.go

View check run for this annotation

Codecov / codecov/patch

pkg/local_object_storage/engine/revive.go#L38

Added line #L38 was not covered by tests
})
return

Check warning on line 40 in pkg/local_object_storage/engine/revive.go

View check run for this annotation

Codecov / codecov/patch

pkg/local_object_storage/engine/revive.go#L40

Added line #L40 was not covered by tests
}
147 changes: 147 additions & 0 deletions pkg/local_object_storage/metabase/revive.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
package meta

import (
"fmt"

"github.com/nspcc-dev/neofs-node/pkg/local_object_storage/util/logicerr"
"github.com/nspcc-dev/neofs-sdk-go/object"
oid "github.com/nspcc-dev/neofs-sdk-go/object/id"
"go.etcd.io/bbolt"
)

// ErrObjectWasNotRemoved is returned when object neither in the graveyard nor was marked with GC mark.
var ErrObjectWasNotRemoved = logicerr.New("object neither in the graveyard nor was marked with GC mark")

// ErrReviveFromContainerGarbage is returned when the object is in the container that marked with GC mark.
var ErrReviveFromContainerGarbage = logicerr.New("revive from container marked with GC mark")

type reviveStatusType int

const (
// ReviveStatusGraveyard is the type of revival status of an object from a graveyard.
ReviveStatusGraveyard reviveStatusType = iota
// ReviveStatusGarbage is the type of revival status of an object from the garbage bucket.
ReviveStatusGarbage
// ReviveStatusError is the type of status when an error occurs during revive.
ReviveStatusError
)

// ReviveStatus groups the resulting values of ReviveObject operation.
// Contains the type of revival status and message for details.
type ReviveStatus struct {
statusType reviveStatusType
message string
}

// Message returns message of status.
func (s *ReviveStatus) Message() string {
return s.message

Check warning on line 38 in pkg/local_object_storage/metabase/revive.go

View check run for this annotation

Codecov / codecov/patch

pkg/local_object_storage/metabase/revive.go#L37-L38

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

// StatusType returns the type of revival status.
func (s *ReviveStatus) StatusType() reviveStatusType {
return s.statusType
}

func (s *ReviveStatus) setStatusGraveyard(tomb string) {
s.statusType = ReviveStatusGraveyard
s.message = fmt.Sprintf("successful revival from graveyard, tomb: %s", tomb)
}

func (s *ReviveStatus) setStatusGarbage() {
s.statusType = ReviveStatusGarbage
s.message = "successful revival from garbage bucket"
}

func (s *ReviveStatus) setStatusError(err error) {
s.statusType = ReviveStatusError
s.message = fmt.Sprintf("didn't revive, err: %v", err)
}

// ReviveObject revives object by oid.Address. Removes GCMark/Tombstone records in the corresponding buckets
// and restore metrics.
func (db *DB) ReviveObject(addr oid.Address) (res ReviveStatus, err error) {
db.modeMtx.RLock()
defer db.modeMtx.RUnlock()

if db.mode.ReadOnly() {
res.setStatusError(ErrReadOnlyMode)
return res, ErrReadOnlyMode

Check warning on line 69 in pkg/local_object_storage/metabase/revive.go

View check run for this annotation

Codecov / codecov/patch

pkg/local_object_storage/metabase/revive.go#L68-L69

Added lines #L68 - L69 were not covered by tests
} else if db.mode.NoMetabase() {
res.setStatusError(ErrDegradedMode)
return res, ErrDegradedMode
}

Check warning on line 73 in pkg/local_object_storage/metabase/revive.go

View check run for this annotation

Codecov / codecov/patch

pkg/local_object_storage/metabase/revive.go#L71-L73

Added lines #L71 - L73 were not covered by tests

currEpoch := db.epochState.CurrentEpoch()

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)

targetKey := addressKey(addr, buf)

if graveyardBKT == nil || garbageObjectsBKT == nil {
// incorrect metabase state, does not make
// sense to check garbage bucket
return ErrObjectWasNotRemoved
}

Check warning on line 90 in pkg/local_object_storage/metabase/revive.go

View check run for this annotation

Codecov / codecov/patch

pkg/local_object_storage/metabase/revive.go#L87-L90

Added lines #L87 - L90 were not covered by tests

val := graveyardBKT.Get(targetKey)
if val != nil {
carpawell marked this conversation as resolved.
Show resolved Hide resolved
// object in the graveyard
if err := graveyardBKT.Delete(targetKey); err != nil {
return err
}

Check warning on line 97 in pkg/local_object_storage/metabase/revive.go

View check run for this annotation

Codecov / codecov/patch

pkg/local_object_storage/metabase/revive.go#L96-L97

Added lines #L96 - L97 were not covered by tests

var tombAddress oid.Address
if err := decodeAddressFromKey(&tombAddress, val[:addressKeySize]); err != nil {
return err
}

Check warning on line 102 in pkg/local_object_storage/metabase/revive.go

View check run for this annotation

Codecov / codecov/patch

pkg/local_object_storage/metabase/revive.go#L101-L102

Added lines #L101 - L102 were not covered by tests
res.setStatusGraveyard(tombAddress.EncodeToString())
} else {
val = garbageContainersBKT.Get(targetKey[:cidSize])
if val != nil {
return ErrReviveFromContainerGarbage
}

Check warning on line 108 in pkg/local_object_storage/metabase/revive.go

View check run for this annotation

Codecov / codecov/patch

pkg/local_object_storage/metabase/revive.go#L107-L108

Added lines #L107 - L108 were not covered by tests

val = garbageObjectsBKT.Get(targetKey)
if val != nil {
// object marked with GC mark
res.setStatusGarbage()
} else {
// neither in the graveyard
// nor was marked with GC mark
return ErrObjectWasNotRemoved
}
}

if err := garbageObjectsBKT.Delete(targetKey); err != nil {
return err
}

Check warning on line 123 in pkg/local_object_storage/metabase/revive.go

View check run for this annotation

Codecov / codecov/patch

pkg/local_object_storage/metabase/revive.go#L122-L123

Added lines #L122 - L123 were not covered by tests

if obj, err := db.get(tx, addr, buf, false, true, currEpoch); err == nil {
// if object is stored, and it is regular object then update bucket
// with container size estimations
if obj.Type() == object.TypeRegular {
if err := changeContainerSize(tx, addr.Container(), obj.PayloadSize(), true); err != nil {
return err
}

Check warning on line 131 in pkg/local_object_storage/metabase/revive.go

View check run for this annotation

Codecov / codecov/patch

pkg/local_object_storage/metabase/revive.go#L130-L131

Added lines #L130 - L131 were not covered by tests
}

// also need to restore logical counter
if err := db.updateCounter(tx, logical, 1, true); err != nil {
return err
}

Check warning on line 137 in pkg/local_object_storage/metabase/revive.go

View check run for this annotation

Codecov / codecov/patch

pkg/local_object_storage/metabase/revive.go#L136-L137

Added lines #L136 - L137 were not covered by tests
}

return nil
})
if err != nil {
res.setStatusError(err)
}

return
}
Loading
Loading