[#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 <leonard@nspcc.ru>
This commit is contained in:
Leonard Lyubich 2021-09-13 11:23:58 +03:00 committed by Alex Vanin
parent 7a15e649ba
commit 55c94a0152
3 changed files with 104 additions and 4 deletions

View file

@ -28,12 +28,53 @@ func (db *DB) Open() error {
return nil return nil
} }
// Init initializes metabase, however metabase doesn't need extra preparations, // Init initializes metabase. It creates static (CID-independent) buckets in underlying BoltDB instance.
// so it implemented to satisfy interface of storage engine components. //
// 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 { 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. // Close closes boltDB instance.

View file

@ -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)
}

View file

@ -6,6 +6,7 @@ import (
cid "github.com/nspcc-dev/neofs-api-go/pkg/container/id" cid "github.com/nspcc-dev/neofs-api-go/pkg/container/id"
"github.com/nspcc-dev/neofs-api-go/pkg/object" "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 { func objectKey(oid *object.ID) []byte {
return []byte(oid.String()) 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)
})
}