From 55c94a0152f3f0bbbb7e9823e1169aee48fd5b22 Mon Sep 17 00:00:00 2001 From: Leonard Lyubich Date: Mon, 13 Sep 2021 11:23:58 +0300 Subject: [PATCH] [#789] metabase: Implement Reset method In the previous implementation of the metabase, there was no possibility of reinitializing the metabase: clearing information about existing objects and bringing it back to its initial state. This operation can be useful in cases when the stored metadata about objects has lost (or possibly lost) relevance, and you need to generate data from scratch. Also at the initialization stage, static resources of the base were not created - container-independent buckets. Make `Metabase.Init` method to allocate graveyard, container-size and to-move-it buckets in underlying BoltDB instance. Implement `Metabase.Reset` method: it works like `Init` but clean up all static buckets and removes other ones. Due to the logical similarity, the methods share a single piece of code. Signed-off-by: Leonard Lyubich --- pkg/local_object_storage/metabase/control.go | 49 +++++++++++++++++-- .../metabase/control_test.go | 47 ++++++++++++++++++ pkg/local_object_storage/metabase/util.go | 12 +++++ 3 files changed, 104 insertions(+), 4 deletions(-) create mode 100644 pkg/local_object_storage/metabase/control_test.go diff --git a/pkg/local_object_storage/metabase/control.go b/pkg/local_object_storage/metabase/control.go index 55769fa3..e876d7cb 100644 --- a/pkg/local_object_storage/metabase/control.go +++ b/pkg/local_object_storage/metabase/control.go @@ -28,12 +28,53 @@ func (db *DB) Open() error { return nil } -// Init initializes metabase, however metabase doesn't need extra preparations, -// so it implemented to satisfy interface of storage engine components. +// Init initializes metabase. It creates static (CID-independent) buckets in underlying BoltDB instance. +// +// Does nothing if metabase has already been initialized and filled. To roll back the database to its initial state, +// use Reset. func (db *DB) Init() error { - db.log.Debug("Metabase has been initialized") + return db.init(false) +} - return nil +// Reset resets metabase. Works similar to Init but cleans up all static buckets and +// removes all dynamic (CID-dependent) ones in non-blank BoltDB instances. +func (db *DB) Reset() error { + return db.init(true) +} + +func (db *DB) init(reset bool) error { + mStaticBuckets := map[string]struct{}{ + string(containerVolumeBucketName): {}, + string(graveyardBucketName): {}, + string(toMoveItBucketName): {}, + } + + return db.boltDB.Update(func(tx *bbolt.Tx) error { + for k := range mStaticBuckets { + b, err := tx.CreateBucketIfNotExists([]byte(k)) + if err != nil { + return fmt.Errorf("could not create static bucket %s: %w", k, err) + } + + if reset { + if err = resetBucket(b); err != nil { + return fmt.Errorf("could not reset static bucket %s: %w", k, err) + } + } + } + + if !reset { + return nil + } + + return tx.ForEach(func(name []byte, b *bbolt.Bucket) error { + if _, ok := mStaticBuckets[string(name)]; !ok { + return tx.DeleteBucket(name) + } + + return nil + }) + }) } // Close closes boltDB instance. diff --git a/pkg/local_object_storage/metabase/control_test.go b/pkg/local_object_storage/metabase/control_test.go new file mode 100644 index 00000000..f4590ebb --- /dev/null +++ b/pkg/local_object_storage/metabase/control_test.go @@ -0,0 +1,47 @@ +package meta_test + +import ( + "testing" + + objectSDK "github.com/nspcc-dev/neofs-api-go/pkg/object" + "github.com/nspcc-dev/neofs-node/pkg/core/object" + meta "github.com/nspcc-dev/neofs-node/pkg/local_object_storage/metabase" + "github.com/stretchr/testify/require" +) + +func TestReset(t *testing.T) { + db := newDB(t) + defer releaseDB(db) + + err := db.Reset() + require.NoError(t, err) + + obj := generateRawObject(t).Object() + addr := obj.Address() + + addrToInhume := generateAddress() + + assertExists := func(addr *objectSDK.Address, expExists bool, expErr error) { + exists, err := meta.Exists(db, addr) + require.ErrorIs(t, err, expErr) + require.Equal(t, expExists, exists) + } + + assertExists(addr, false, nil) + assertExists(addrToInhume, false, nil) + + err = putBig(db, obj) + require.NoError(t, err) + + err = meta.Inhume(db, addrToInhume, generateAddress()) + require.NoError(t, err) + + assertExists(addr, true, nil) + assertExists(addrToInhume, false, object.ErrAlreadyRemoved) + + err = db.Reset() + require.NoError(t, err) + + assertExists(addr, false, nil) + assertExists(addr, false, nil) +} diff --git a/pkg/local_object_storage/metabase/util.go b/pkg/local_object_storage/metabase/util.go index 2b20c7e2..33ce879a 100644 --- a/pkg/local_object_storage/metabase/util.go +++ b/pkg/local_object_storage/metabase/util.go @@ -6,6 +6,7 @@ import ( cid "github.com/nspcc-dev/neofs-api-go/pkg/container/id" "github.com/nspcc-dev/neofs-api-go/pkg/object" + "go.etcd.io/bbolt" ) /* @@ -118,3 +119,14 @@ func addressFromKey(k []byte) (*object.Address, error) { func objectKey(oid *object.ID) []byte { return []byte(oid.String()) } + +// removes all bucket elements. +func resetBucket(b *bbolt.Bucket) error { + return b.ForEach(func(k, v []byte) error { + if v != nil { + return b.Delete(k) + } + + return b.DeleteBucket(k) + }) +}