Skip to content

Commit

Permalink
lens, metabase: save last epoch when metabase was resynchronized
Browse files Browse the repository at this point in the history
Add new key `last_resync_epoch` to `shardInfoBucket` in metabase `boltDB` and
new field in `Info` that contains last epoch when metabase was resynchronized.
Update this value in db when the metabase is `Reset` and read on `init`.
Also make `lens` command `meta last-resync-epoch` to read this value from
metabase path. Add test. Update VERSION.md.

Closes #2887.

Signed-off-by: Andrey Butusov <[email protected]>
  • Loading branch information
End-rey committed Oct 10, 2024
1 parent 530db9a commit 1b3e811
Show file tree
Hide file tree
Showing 8 changed files with 168 additions and 11 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ Changelog for NeoFS Node
- `node` config option `storage.ignore_uninited_shards` (#2953)
- For `neofs-cli container create`, add `--global-name` flag, that sets name attribute as the value of `__NEOFS__NAME`
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)

### Fixed
- Do not search for tombstones when handling their expiration, use local indexes instead (#2929)
Expand Down
33 changes: 33 additions & 0 deletions cmd/neofs-lens/internal/meta/last_resync_epoch.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package meta

import (
common "github.com/nspcc-dev/neofs-node/cmd/neofs-lens/internal"
"github.com/spf13/cobra"
)

var lastResyncEpochCMD = &cobra.Command{
Use: "last-resync-epoch",
Short: "Read last epoch when metabase was resynchronized",
Args: cobra.NoArgs,
RunE: lastResyncEpochFunc,
}

func init() {
common.AddComponentPathFlag(lastResyncEpochCMD, &vPath)

Check warning on line 16 in cmd/neofs-lens/internal/meta/last_resync_epoch.go

View check run for this annotation

Codecov / codecov/patch

cmd/neofs-lens/internal/meta/last_resync_epoch.go#L15-L16

Added lines #L15 - L16 were not covered by tests
}

func lastResyncEpochFunc(cmd *cobra.Command, _ []string) error {
db, err := openMeta(true)
if err != nil {
return err

Check warning on line 22 in cmd/neofs-lens/internal/meta/last_resync_epoch.go

View check run for this annotation

Codecov / codecov/patch

cmd/neofs-lens/internal/meta/last_resync_epoch.go#L19-L22

Added lines #L19 - L22 were not covered by tests
}
defer db.Close()

Check warning on line 24 in cmd/neofs-lens/internal/meta/last_resync_epoch.go

View check run for this annotation

Codecov / codecov/patch

cmd/neofs-lens/internal/meta/last_resync_epoch.go#L24

Added line #L24 was not covered by tests

epoch, err := db.ReadLastResyncEpoch()
if err != nil {
return err

Check warning on line 28 in cmd/neofs-lens/internal/meta/last_resync_epoch.go

View check run for this annotation

Codecov / codecov/patch

cmd/neofs-lens/internal/meta/last_resync_epoch.go#L26-L28

Added lines #L26 - L28 were not covered by tests
}
cmd.Println(epoch)

Check warning on line 30 in cmd/neofs-lens/internal/meta/last_resync_epoch.go

View check run for this annotation

Codecov / codecov/patch

cmd/neofs-lens/internal/meta/last_resync_epoch.go#L30

Added line #L30 was not covered by tests

return nil

Check warning on line 32 in cmd/neofs-lens/internal/meta/last_resync_epoch.go

View check run for this annotation

Codecov / codecov/patch

cmd/neofs-lens/internal/meta/last_resync_epoch.go#L32

Added line #L32 was not covered by tests
}
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 @@ -36,6 +36,7 @@ func init() {
writeObjectCMD,
getCMD,
statCMD,
lastResyncEpochCMD,

Check warning on line 39 in cmd/neofs-lens/internal/meta/root.go

View check run for this annotation

Codecov / codecov/patch

cmd/neofs-lens/internal/meta/root.go#L39

Added line #L39 was not covered by tests
)
}

Expand Down
1 change: 1 addition & 0 deletions pkg/local_object_storage/metabase/VERSION.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ The lowest not used bucket index: 20.
- `version` -> metabase version as little-endian uint64
- `phy_counter` -> shard's physical object counter as little-endian uint64
- `logic_counter` -> shard's logical object counter as little-endian uint64
- `last_resync_epoch` -> last epoch when metabase was resynchronized as little-endian uint64

### Unique index buckets
- Bucket containing objects of REGULAR type
Expand Down
32 changes: 21 additions & 11 deletions pkg/local_object_storage/metabase/control.go
Original file line number Diff line number Diff line change
Expand Up @@ -138,21 +138,31 @@ func (db *DB) init(reset bool) error {
if err != nil {
return fmt.Errorf("could not sync object counter: %w", err)
}
} else {
err = tx.ForEach(func(name []byte, b *bbolt.Bucket) error {
if _, ok := mStaticBuckets[string(name)]; !ok {
return tx.DeleteBucket(name)
}

return nil
}

err = tx.ForEach(func(name []byte, b *bbolt.Bucket) error {
if _, ok := mStaticBuckets[string(name)]; !ok {
return tx.DeleteBucket(name)
return nil
})
if err != nil {
return err

Check warning on line 150 in pkg/local_object_storage/metabase/control.go

View check run for this annotation

Codecov / codecov/patch

pkg/local_object_storage/metabase/control.go#L150

Added line #L150 was not covered by tests
}

return nil
})
if err != nil {
return err
err = updateLastResyncEpoch(tx, db.epochState.CurrentEpoch())
if err != nil {
return err

Check warning on line 155 in pkg/local_object_storage/metabase/control.go

View check run for this annotation

Codecov / codecov/patch

pkg/local_object_storage/metabase/control.go#L155

Added line #L155 was not covered by tests
}
err = updateVersion(tx, version)
if err != nil {
return err

Check warning on line 159 in pkg/local_object_storage/metabase/control.go

View check run for this annotation

Codecov / codecov/patch

pkg/local_object_storage/metabase/control.go#L159

Added line #L159 was not covered by tests
}
}
return updateVersion(tx, version)

db.info.LastResyncEpoch = getLastResyncEpoch(tx)

return nil
})
}

Expand Down
3 changes: 3 additions & 0 deletions pkg/local_object_storage/metabase/info.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ type Info struct {

// Permission of database file.
Permission fs.FileMode

// LastResyncEpoch is a last epoch when metabase was resynchronized.
LastResyncEpoch uint64
}

// DumpInfo returns information about the DB.
Expand Down
52 changes: 52 additions & 0 deletions pkg/local_object_storage/metabase/last_resync_epoch.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package meta

import (
"encoding/binary"
"fmt"

"go.etcd.io/bbolt"
)

var lastResyncEpochKey = []byte("last_resync_epoch")

// ReadLastResyncEpoch reads from db last epoch when metabase was resynchronized.
// If id is missing, returns 0, nil.
func (db *DB) ReadLastResyncEpoch() (epoch uint64, err error) {
db.modeMtx.RLock()
defer db.modeMtx.RUnlock()

if db.mode.NoMetabase() {
return 0, ErrDegradedMode

Check warning on line 19 in pkg/local_object_storage/metabase/last_resync_epoch.go

View check run for this annotation

Codecov / codecov/patch

pkg/local_object_storage/metabase/last_resync_epoch.go#L19

Added line #L19 was not covered by tests
}

err = db.boltDB.View(func(tx *bbolt.Tx) error {
epoch = getLastResyncEpoch(tx)
return nil
})

return
}

// updateLastResyncEpoch updates db value of last epoch when metabase was resynchronized.
func updateLastResyncEpoch(tx *bbolt.Tx, epoch uint64) error {
data := make([]byte, 8)
binary.LittleEndian.PutUint64(data, epoch)

b, err := tx.CreateBucketIfNotExists(shardInfoBucket)
if err != nil {
return fmt.Errorf("can't create auxiliary bucket: %w", err)

Check warning on line 37 in pkg/local_object_storage/metabase/last_resync_epoch.go

View check run for this annotation

Codecov / codecov/patch

pkg/local_object_storage/metabase/last_resync_epoch.go#L37

Added line #L37 was not covered by tests
}
return b.Put(lastResyncEpochKey, data)
}

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

return 0
}
55 changes: 55 additions & 0 deletions pkg/local_object_storage/metabase/last_resync_epoch_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package meta_test

import (
"path/filepath"
"testing"

meta "github.com/nspcc-dev/neofs-node/pkg/local_object_storage/metabase"
"github.com/stretchr/testify/require"
)

func TestDB_ReadLastResyncEpoch(t *testing.T) {
es := &epochState{
e: currEpoch,
}

db := meta.New([]meta.Option{
meta.WithPath(filepath.Join(t.TempDir(), "meta")),
meta.WithPermissions(0o600),
meta.WithEpochState(es),
}...)

require.NoError(t, db.Open(false))
require.NoError(t, db.Init())

t.Cleanup(func() {
db.Close()
})

checkEpoch := func(t *testing.T, epoch uint64) {
gotEpoch, err := db.ReadLastResyncEpoch()
require.NoError(t, err)
require.Equal(t, epoch, gotEpoch)
require.Equal(t, epoch, db.DumpInfo().LastResyncEpoch)
}

// Clean db without resynchronizations, so it is 0.
checkEpoch(t, 0)

// After Reset, last resync epoch == current epoch.
require.NoError(t, db.Reset())

checkEpoch(t, currEpoch)

// Two epochs tick, last resync epoch the same.
es.e = currEpoch + 2

checkEpoch(t, currEpoch)

// After reload, last resync epoch the same.
require.NoError(t, db.Close())
require.NoError(t, db.Open(false))
require.NoError(t, db.Init())

checkEpoch(t, currEpoch)
}

0 comments on commit 1b3e811

Please sign in to comment.