[#959] node: Set mode to shard's components when open it

Avoid opening database for `metabase` and `cache` in `Degraded` mode.

Signed-off-by: Anton Nikiforov <an.nikiforov@yadro.com>
This commit is contained in:
Anton Nikiforov 2024-02-09 09:17:17 +03:00 committed by Evgenii Stratonikov
parent 60527abb65
commit d19ade23c8
25 changed files with 219 additions and 74 deletions

View file

@ -5,6 +5,7 @@ import (
common "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-lens/internal" common "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-lens/internal"
meta "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/metabase" meta "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/metabase"
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/shard/mode"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"go.etcd.io/bbolt" "go.etcd.io/bbolt"
) )
@ -43,7 +44,7 @@ func openMeta(cmd *cobra.Command) *meta.DB {
}), }),
meta.WithEpochState(epochState{}), meta.WithEpochState(epochState{}),
) )
common.ExitOnErr(cmd, common.Errf("could not open metabase: %w", db.Open(cmd.Context(), true))) common.ExitOnErr(cmd, common.Errf("could not open metabase: %w", db.Open(cmd.Context(), mode.ReadOnly)))
return db return db
} }

View file

@ -11,6 +11,7 @@ import (
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/blobstor/common" "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/blobstor/common"
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/blobstor/fstree" "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/blobstor/fstree"
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/blobstor/teststore" "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/blobstor/teststore"
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/shard/mode"
objectSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object" objectSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
@ -51,7 +52,7 @@ func TestCompression(t *testing.T) {
bs := New( bs := New(
WithCompressObjects(compress), WithCompressObjects(compress),
WithStorages(defaultStorages(dir, smallSizeLimit))) WithStorages(defaultStorages(dir, smallSizeLimit)))
require.NoError(t, bs.Open(context.Background(), false)) require.NoError(t, bs.Open(context.Background(), mode.ReadWrite))
require.NoError(t, bs.Init()) require.NoError(t, bs.Init())
return bs return bs
} }
@ -126,7 +127,7 @@ func TestBlobstor_needsCompression(t *testing.T) {
Storage: fstree.New(fstree.WithPath(dir)), Storage: fstree.New(fstree.WithPath(dir)),
}, },
})) }))
require.NoError(t, bs.Open(context.Background(), false)) require.NoError(t, bs.Open(context.Background(), mode.ReadWrite))
require.NoError(t, bs.Init()) require.NoError(t, bs.Init())
return bs return bs
} }
@ -188,7 +189,7 @@ func TestConcurrentPut(t *testing.T) {
blobStor := New( blobStor := New(
WithStorages(defaultStorages(dir, smallSizeLimit))) WithStorages(defaultStorages(dir, smallSizeLimit)))
require.NoError(t, blobStor.Open(context.Background(), false)) require.NoError(t, blobStor.Open(context.Background(), mode.ReadWrite))
require.NoError(t, blobStor.Init()) require.NoError(t, blobStor.Init())
testGet := func(t *testing.T, b *BlobStor, obj *objectSDK.Object) { testGet := func(t *testing.T, b *BlobStor, obj *objectSDK.Object) {
@ -268,7 +269,7 @@ func TestConcurrentDelete(t *testing.T) {
blobStor := New( blobStor := New(
WithStorages(defaultStorages(dir, smallSizeLimit))) WithStorages(defaultStorages(dir, smallSizeLimit)))
require.NoError(t, blobStor.Open(context.Background(), false)) require.NoError(t, blobStor.Open(context.Background(), mode.ReadWrite))
require.NoError(t, blobStor.Init()) require.NoError(t, blobStor.Init())
testPut := func(t *testing.T, b *BlobStor, obj *objectSDK.Object) { testPut := func(t *testing.T, b *BlobStor, obj *objectSDK.Object) {

View file

@ -6,13 +6,29 @@ import (
"fmt" "fmt"
"git.frostfs.info/TrueCloudLab/frostfs-node/internal/logs" "git.frostfs.info/TrueCloudLab/frostfs-node/internal/logs"
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/shard/mode"
"go.uber.org/zap" "go.uber.org/zap"
) )
// Open opens BlobStor. // Open opens BlobStor.
func (b *BlobStor) Open(ctx context.Context, readOnly bool) error { func (b *BlobStor) Open(ctx context.Context, mode mode.Mode) error {
b.log.Debug(logs.BlobstorOpening) b.log.Debug(logs.BlobstorOpening)
b.modeMtx.Lock()
defer b.modeMtx.Unlock()
b.mode = mode
err := b.openBlobStor(ctx, mode)
if err != nil {
return err
}
b.metrics.SetMode(mode.ReadOnly())
return nil
}
func (b *BlobStor) openBlobStor(ctx context.Context, mode mode.Mode) error {
readOnly := mode.ReadOnly()
for i := range b.storage { for i := range b.storage {
select { select {
case <-ctx.Done(): case <-ctx.Done():
@ -24,7 +40,6 @@ func (b *BlobStor) Open(ctx context.Context, readOnly bool) error {
return err return err
} }
} }
b.metrics.SetMode(readOnly)
return nil return nil
} }

View file

@ -7,6 +7,7 @@ import (
objectCore "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/core/object" objectCore "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/core/object"
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/blobstor/common" "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/blobstor/common"
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/blobstor/teststore" "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/blobstor/teststore"
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/shard/mode"
cidtest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id/test" cidtest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id/test"
objectSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object" objectSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object"
oidtest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id/test" oidtest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id/test"
@ -20,7 +21,7 @@ func TestExists(t *testing.T) {
b := New(WithStorages(storages)) b := New(WithStorages(storages))
require.NoError(t, b.Open(context.Background(), false)) require.NoError(t, b.Open(context.Background(), mode.ReadWrite))
require.NoError(t, b.Init()) require.NoError(t, b.Init())
objects := []*objectSDK.Object{ objects := []*objectSDK.Object{

View file

@ -7,6 +7,7 @@ import (
"testing" "testing"
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/blobstor/common" "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/blobstor/common"
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/shard/mode"
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id" oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
oidtest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id/test" oidtest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id/test"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
@ -26,7 +27,7 @@ func TestIterateObjects(t *testing.T) {
defer os.RemoveAll(p) defer os.RemoveAll(p)
// open Blobstor // open Blobstor
require.NoError(t, blobStor.Open(context.Background(), false)) require.NoError(t, blobStor.Open(context.Background(), mode.ReadWrite))
// initialize Blobstor // initialize Blobstor
require.NoError(t, blobStor.Init()) require.NoError(t, blobStor.Init())

View file

@ -22,7 +22,7 @@ func (b *BlobStor) SetMode(m mode.Mode) error {
err := b.Close() err := b.Close()
if err == nil { if err == nil {
if err = b.Open(context.TODO(), m.ReadOnly()); err == nil { if err = b.openBlobStor(context.TODO(), m); err == nil {
err = b.Init() err = b.Init()
} }
} }

View file

@ -10,7 +10,7 @@ import (
// Component represents single storage component. // Component represents single storage component.
type Component interface { type Component interface {
Open(context.Context, bool) error Open(context.Context, mode.Mode) error
SetMode(mode.Mode) error SetMode(mode.Mode) error
Init() error Init() error
Close() error Close() error
@ -58,18 +58,18 @@ func TestCloseAfterOpen(t *testing.T, cons Constructor) {
t.Run("RW", func(t *testing.T) { t.Run("RW", func(t *testing.T) {
// Use-case: irrecoverable error on some components, close everything. // Use-case: irrecoverable error on some components, close everything.
s := cons(t) s := cons(t)
require.NoError(t, s.Open(context.Background(), false)) require.NoError(t, s.Open(context.Background(), mode.ReadWrite))
require.NoError(t, s.Close()) require.NoError(t, s.Close())
}) })
t.Run("RO", func(t *testing.T) { t.Run("RO", func(t *testing.T) {
// Use-case: irrecoverable error on some components, close everything. // Use-case: irrecoverable error on some components, close everything.
// Open in read-only must be done after the db is here. // Open in read-only must be done after the db is here.
s := cons(t) s := cons(t)
require.NoError(t, s.Open(context.Background(), false)) require.NoError(t, s.Open(context.Background(), mode.ReadWrite))
require.NoError(t, s.Init()) require.NoError(t, s.Init())
require.NoError(t, s.Close()) require.NoError(t, s.Close())
require.NoError(t, s.Open(context.Background(), true)) require.NoError(t, s.Open(context.Background(), mode.ReadOnly))
require.NoError(t, s.Close()) require.NoError(t, s.Close())
}) })
} }
@ -78,7 +78,7 @@ func TestCloseAfterOpen(t *testing.T, cons Constructor) {
func TestCloseTwice(t *testing.T, cons Constructor) { func TestCloseTwice(t *testing.T, cons Constructor) {
// Use-case: move to maintenance mode twice, first time failed. // Use-case: move to maintenance mode twice, first time failed.
s := cons(t) s := cons(t)
require.NoError(t, s.Open(context.Background(), false)) require.NoError(t, s.Open(context.Background(), mode.ReadWrite))
require.NoError(t, s.Init()) require.NoError(t, s.Init())
require.NoError(t, s.Close()) require.NoError(t, s.Close())
require.NoError(t, s.Close()) // already closed, no-op require.NoError(t, s.Close()) // already closed, no-op
@ -90,12 +90,12 @@ func TestSetMode(t *testing.T, cons Constructor, m mode.Mode) {
// Use-case: metabase `Init` failed, // Use-case: metabase `Init` failed,
// call `SetMode` on all not-yet-initialized components. // call `SetMode` on all not-yet-initialized components.
s := cons(t) s := cons(t)
require.NoError(t, s.Open(context.Background(), false)) require.NoError(t, s.Open(context.Background(), mode.ReadWrite))
require.NoError(t, s.SetMode(m)) require.NoError(t, s.SetMode(m))
t.Run("after open in RO", func(t *testing.T) { t.Run("after open in RO", func(t *testing.T) {
require.NoError(t, s.Close()) require.NoError(t, s.Close())
require.NoError(t, s.Open(context.Background(), true)) require.NoError(t, s.Open(context.Background(), mode.ReadOnly))
require.NoError(t, s.SetMode(m)) require.NoError(t, s.SetMode(m))
}) })
@ -104,7 +104,7 @@ func TestSetMode(t *testing.T, cons Constructor, m mode.Mode) {
t.Run("after init", func(t *testing.T) { t.Run("after init", func(t *testing.T) {
s := cons(t) s := cons(t)
// Use-case: notmal node operation. // Use-case: notmal node operation.
require.NoError(t, s.Open(context.Background(), false)) require.NoError(t, s.Open(context.Background(), mode.ReadWrite))
require.NoError(t, s.Init()) require.NoError(t, s.Init())
require.NoError(t, s.SetMode(m)) require.NoError(t, s.SetMode(m))
require.NoError(t, s.Close()) require.NoError(t, s.Close())
@ -114,7 +114,7 @@ func TestSetMode(t *testing.T, cons Constructor, m mode.Mode) {
func TestModeTransition(t *testing.T, cons Constructor, from, to mode.Mode) { func TestModeTransition(t *testing.T, cons Constructor, from, to mode.Mode) {
// Use-case: normal node operation. // Use-case: normal node operation.
s := cons(t) s := cons(t)
require.NoError(t, s.Open(context.Background(), false)) require.NoError(t, s.Open(context.Background(), mode.ReadWrite))
require.NoError(t, s.Init()) require.NoError(t, s.Init())
require.NoError(t, s.SetMode(from)) require.NoError(t, s.SetMode(from))
require.NoError(t, s.SetMode(to)) require.NoError(t, s.SetMode(to))

View file

@ -22,7 +22,18 @@ var ErrDegradedMode = logicerr.New("metabase is in a degraded mode")
var ErrReadOnlyMode = logicerr.New("metabase is in a read-only mode") var ErrReadOnlyMode = logicerr.New("metabase is in a read-only mode")
// Open boltDB instance for metabase. // Open boltDB instance for metabase.
func (db *DB) Open(_ context.Context, readOnly bool) error { func (db *DB) Open(_ context.Context, mode mode.Mode) error {
db.modeMtx.Lock()
defer db.modeMtx.Unlock()
db.mode = mode
if mode.NoMetabase() {
return nil
}
return db.openDB(mode)
}
func (db *DB) openDB(mode mode.Mode) error {
err := util.MkdirAllX(filepath.Dir(db.info.Path), db.info.Permission) err := util.MkdirAllX(filepath.Dir(db.info.Path), db.info.Permission)
if err != nil { if err != nil {
return fmt.Errorf("can't create dir %s for metabase: %w", db.info.Path, err) return fmt.Errorf("can't create dir %s for metabase: %w", db.info.Path, err)
@ -34,7 +45,7 @@ func (db *DB) Open(_ context.Context, readOnly bool) error {
opts := *bbolt.DefaultOptions opts := *bbolt.DefaultOptions
db.boltOptions = &opts db.boltOptions = &opts
} }
db.boltOptions.ReadOnly = readOnly db.boltOptions.ReadOnly = mode.ReadOnly()
return metaerr.Wrap(db.openBolt()) return metaerr.Wrap(db.openBolt())
} }

View file

@ -9,6 +9,7 @@ import (
objectV2 "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/object" objectV2 "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/object"
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/internal/testutil" "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/internal/testutil"
meta "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/metabase" meta "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/metabase"
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/shard/mode"
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id" cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
objectSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object" objectSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object"
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id" oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
@ -49,7 +50,7 @@ func newDB(t testing.TB, opts ...meta.Option) *meta.DB {
}, opts...)..., }, opts...)...,
) )
require.NoError(t, bdb.Open(context.Background(), false)) require.NoError(t, bdb.Open(context.Background(), mode.ReadWrite))
require.NoError(t, bdb.Init()) require.NoError(t, bdb.Init())
return bdb return bdb

View file

@ -1,7 +1,6 @@
package meta package meta
import ( import (
"context"
"fmt" "fmt"
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/shard/mode" "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/shard/mode"
@ -23,21 +22,16 @@ func (db *DB) SetMode(m mode.Mode) error {
} }
} }
var err error if m.NoMetabase() {
switch {
case m.NoMetabase():
db.boltDB = nil db.boltDB = nil
case m.ReadOnly(): } else {
err = db.Open(context.TODO(), true) err := db.openDB(m)
default: if err == nil && !m.ReadOnly() {
err = db.Open(context.TODO(), false) err = db.Init()
} }
if err == nil && !m.NoMetabase() && !m.ReadOnly() { if err != nil {
err = db.Init() return fmt.Errorf("can't set metabase mode (old=%s, new=%s): %w", db.mode, m, err)
} }
if err != nil {
return fmt.Errorf("can't set metabase mode (old=%s, new=%s): %w", db.mode, m, err)
} }
db.mode = m db.mode = m

View file

@ -0,0 +1,37 @@
package meta
import (
"context"
"path/filepath"
"testing"
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/shard/mode"
"github.com/stretchr/testify/require"
)
type epochStateTest struct{}
func (s epochStateTest) CurrentEpoch() uint64 {
return 0
}
func Test_Mode(t *testing.T) {
t.Parallel()
bdb := New([]Option{
WithPath(filepath.Join(t.TempDir(), "metabase")),
WithPermissions(0o600),
WithEpochState(epochStateTest{}),
}...)
require.NoError(t, bdb.Open(context.Background(), mode.DegradedReadOnly))
require.Nil(t, bdb.boltDB)
require.NoError(t, bdb.Init())
require.Nil(t, bdb.boltDB)
require.NoError(t, bdb.Close())
require.NoError(t, bdb.Open(context.Background(), mode.Degraded))
require.Nil(t, bdb.boltDB)
require.NoError(t, bdb.Init())
require.Nil(t, bdb.boltDB)
require.NoError(t, bdb.Close())
}

View file

@ -8,6 +8,7 @@ import (
"path/filepath" "path/filepath"
"testing" "testing"
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/shard/mode"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"go.etcd.io/bbolt" "go.etcd.io/bbolt"
) )
@ -43,13 +44,13 @@ func TestVersion(t *testing.T) {
} }
t.Run("simple", func(t *testing.T) { t.Run("simple", func(t *testing.T) {
db := newDB(t) db := newDB(t)
require.NoError(t, db.Open(context.Background(), false)) require.NoError(t, db.Open(context.Background(), mode.ReadWrite))
require.NoError(t, db.Init()) require.NoError(t, db.Init())
check(t, db) check(t, db)
require.NoError(t, db.Close()) require.NoError(t, db.Close())
t.Run("reopen", func(t *testing.T) { t.Run("reopen", func(t *testing.T) {
require.NoError(t, db.Open(context.Background(), false)) require.NoError(t, db.Open(context.Background(), mode.ReadWrite))
require.NoError(t, db.Init()) require.NoError(t, db.Init())
check(t, db) check(t, db)
require.NoError(t, db.Close()) require.NoError(t, db.Close())
@ -57,29 +58,29 @@ func TestVersion(t *testing.T) {
}) })
t.Run("old data", func(t *testing.T) { t.Run("old data", func(t *testing.T) {
db := newDB(t) db := newDB(t)
require.NoError(t, db.Open(context.Background(), false)) require.NoError(t, db.Open(context.Background(), mode.ReadWrite))
require.NoError(t, db.WriteShardID([]byte{1, 2, 3, 4})) require.NoError(t, db.WriteShardID([]byte{1, 2, 3, 4}))
require.NoError(t, db.Close()) require.NoError(t, db.Close())
require.NoError(t, db.Open(context.Background(), false)) require.NoError(t, db.Open(context.Background(), mode.ReadWrite))
require.NoError(t, db.Init()) require.NoError(t, db.Init())
check(t, db) check(t, db)
require.NoError(t, db.Close()) require.NoError(t, db.Close())
}) })
t.Run("invalid version", func(t *testing.T) { t.Run("invalid version", func(t *testing.T) {
db := newDB(t) db := newDB(t)
require.NoError(t, db.Open(context.Background(), false)) require.NoError(t, db.Open(context.Background(), mode.ReadWrite))
require.NoError(t, db.boltDB.Update(func(tx *bbolt.Tx) error { require.NoError(t, db.boltDB.Update(func(tx *bbolt.Tx) error {
return updateVersion(tx, version+1) return updateVersion(tx, version+1)
})) }))
require.NoError(t, db.Close()) require.NoError(t, db.Close())
require.NoError(t, db.Open(context.Background(), false)) require.NoError(t, db.Open(context.Background(), mode.ReadWrite))
require.Error(t, db.Init()) require.Error(t, db.Init())
require.NoError(t, db.Close()) require.NoError(t, db.Close())
t.Run("reset", func(t *testing.T) { t.Run("reset", func(t *testing.T) {
require.NoError(t, db.Open(context.Background(), false)) require.NoError(t, db.Open(context.Background(), mode.ReadWrite))
require.NoError(t, db.Reset()) require.NoError(t, db.Reset())
check(t, db) check(t, db)
require.NoError(t, db.Close()) require.NoError(t, db.Close())

View file

@ -8,6 +8,7 @@ import (
"sync/atomic" "sync/atomic"
"testing" "testing"
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/shard/mode"
cidtest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id/test" cidtest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id/test"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
@ -26,7 +27,7 @@ func BenchmarkCreate(b *testing.B) {
f := NewBoltForest( f := NewBoltForest(
WithPath(filepath.Join(tmpDir, "test.db")), WithPath(filepath.Join(tmpDir, "test.db")),
WithMaxBatchSize(runtime.GOMAXPROCS(0))) WithMaxBatchSize(runtime.GOMAXPROCS(0)))
require.NoError(b, f.Open(context.Background(), false)) require.NoError(b, f.Open(context.Background(), mode.ReadWrite))
require.NoError(b, f.Init()) require.NoError(b, f.Init())
defer func() { require.NoError(b, f.Close()) }() defer func() { require.NoError(b, f.Close()) }()

View file

@ -99,7 +99,7 @@ func (t *boltForest) SetMode(m mode.Mode) error {
err := t.Close() err := t.Close()
if err == nil && !m.NoMetabase() { if err == nil && !m.NoMetabase() {
if err = t.Open(context.TODO(), m.ReadOnly()); err == nil { if err = t.openBolt(m); err == nil {
err = t.Init() err = t.Init()
} }
} }
@ -112,7 +112,18 @@ func (t *boltForest) SetMode(m mode.Mode) error {
return nil return nil
} }
func (t *boltForest) Open(_ context.Context, readOnly bool) error { func (t *boltForest) Open(_ context.Context, mode mode.Mode) error {
t.modeMtx.Lock()
defer t.modeMtx.Unlock()
t.mode = mode
if mode.NoMetabase() {
return nil
}
return t.openBolt(mode)
}
func (t *boltForest) openBolt(mode mode.Mode) error {
readOnly := mode.ReadOnly()
err := util.MkdirAllX(filepath.Dir(t.path), t.perm) err := util.MkdirAllX(filepath.Dir(t.path), t.perm)
if err != nil { if err != nil {
return metaerr.Wrap(fmt.Errorf("can't create dir %s for the pilorama: %w", t.path, err)) return metaerr.Wrap(fmt.Errorf("can't create dir %s for the pilorama: %w", t.path, err))
@ -131,11 +142,7 @@ func (t *boltForest) Open(_ context.Context, readOnly bool) error {
t.db.MaxBatchSize = t.maxBatchSize t.db.MaxBatchSize = t.maxBatchSize
t.db.MaxBatchDelay = t.maxBatchDelay t.db.MaxBatchDelay = t.maxBatchDelay
m := mode.ReadWrite t.metrics.SetMode(mode)
if readOnly {
m = mode.ReadOnly
}
t.metrics.SetMode(m)
return nil return nil
} }

View file

@ -112,7 +112,7 @@ func (f *memoryForest) Init() error {
return nil return nil
} }
func (f *memoryForest) Open(context.Context, bool) error { func (f *memoryForest) Open(context.Context, mode.Mode) error {
return nil return nil
} }

View file

@ -11,6 +11,7 @@ import (
"testing" "testing"
"time" "time"
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/shard/mode"
cidSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id" cidSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
cidtest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id/test" cidtest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id/test"
objectSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object" objectSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object"
@ -24,7 +25,7 @@ var providers = []struct {
}{ }{
{"inmemory", func(t testing.TB, _ ...Option) ForestStorage { {"inmemory", func(t testing.TB, _ ...Option) ForestStorage {
f := NewMemoryForest() f := NewMemoryForest()
require.NoError(t, f.Open(context.Background(), false)) require.NoError(t, f.Open(context.Background(), mode.ReadWrite))
require.NoError(t, f.Init()) require.NoError(t, f.Init())
return f return f
}}, }},
@ -34,7 +35,7 @@ var providers = []struct {
WithPath(filepath.Join(t.TempDir(), "test.db")), WithPath(filepath.Join(t.TempDir(), "test.db")),
WithMaxBatchSize(1), WithMaxBatchSize(1),
}, opts...)...) }, opts...)...)
require.NoError(t, f.Open(context.Background(), false)) require.NoError(t, f.Open(context.Background(), mode.ReadWrite))
require.NoError(t, f.Init()) require.NoError(t, f.Init())
return f return f
}}, }},

View file

@ -58,7 +58,7 @@ type ForestStorage interface {
// DumpInfo returns information about the pilorama. // DumpInfo returns information about the pilorama.
DumpInfo() Info DumpInfo() Info
Init() error Init() error
Open(context.Context, bool) error Open(context.Context, mode.Mode) error
Close() error Close() error
SetMode(m mode.Mode) error SetMode(m mode.Mode) error
SetParentID(id string) SetParentID(id string)

View file

@ -0,0 +1,31 @@
package pilorama
import (
"context"
"path/filepath"
"testing"
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/shard/mode"
"github.com/stretchr/testify/require"
)
func Test_Mode(t *testing.T) {
t.Parallel()
f := NewBoltForest(
[]Option{
WithPath(filepath.Join(t.TempDir(), "test.db")),
WithMaxBatchSize(1),
}...)
require.NoError(t, f.Open(context.Background(), mode.DegradedReadOnly))
require.Nil(t, f.(*boltForest).db)
require.NoError(t, f.Init())
require.Nil(t, f.(*boltForest).db)
require.NoError(t, f.Close())
require.NoError(t, f.Open(context.Background(), mode.Degraded))
require.Nil(t, f.(*boltForest).db)
require.NoError(t, f.Init())
require.Nil(t, f.(*boltForest).db)
require.NoError(t, f.Close())
}

View file

@ -43,12 +43,17 @@ func (s *Shard) handleMetabaseFailure(stage string, err error) error {
// Open opens all Shard's components. // Open opens all Shard's components.
func (s *Shard) Open(ctx context.Context) error { func (s *Shard) Open(ctx context.Context) error {
components := []interface { components := []interface {
Open(context.Context, bool) error Open(context.Context, mode.Mode) error
}{ }{
s.blobStor, s.metaBase, s.blobStor,
}
m := s.GetMode()
if !m.NoMetabase() {
components = append(components, s.metaBase)
} }
if s.hasWriteCache() { if s.hasWriteCache() && !m.NoMetabase() {
components = append(components, s.writeCache) components = append(components, s.writeCache)
} }
@ -57,12 +62,12 @@ func (s *Shard) Open(ctx context.Context) error {
} }
for i, component := range components { for i, component := range components {
if err := component.Open(ctx, false); err != nil { if err := component.Open(ctx, m); err != nil {
if component == s.metaBase { if component == s.metaBase {
// We must first open all other components to avoid // We must first open all other components to avoid
// opening non-existent DB in read-only mode. // opening non-existent DB in read-only mode.
for j := i + 1; j < len(components); j++ { for j := i + 1; j < len(components); j++ {
if err := components[j].Open(ctx, false); err != nil { if err := components[j].Open(ctx, m); err != nil {
// Other components must be opened, fail. // Other components must be opened, fail.
return fmt.Errorf("could not open %T: %w", components[j], err) return fmt.Errorf("could not open %T: %w", components[j], err)
} }
@ -97,8 +102,9 @@ func (s *Shard) Init(ctx context.Context) error {
} }
var components []initializer var components []initializer
m := s.GetMode()
if !s.GetMode().NoMetabase() { if !m.NoMetabase() {
var initMetabase initializer var initMetabase initializer
if s.NeedRefillMetabase() { if s.NeedRefillMetabase() {
@ -114,7 +120,7 @@ func (s *Shard) Init(ctx context.Context) error {
components = []initializer{s.blobStor} components = []initializer{s.blobStor}
} }
if s.hasWriteCache() { if s.hasWriteCache() && !m.NoMetabase() {
components = append(components, s.writeCache) components = append(components, s.writeCache)
} }

View file

@ -31,12 +31,11 @@ func (s *Shard) ID() *ID {
// UpdateID reads shard ID saved in the metabase and updates it if it is missing. // UpdateID reads shard ID saved in the metabase and updates it if it is missing.
func (s *Shard) UpdateID(ctx context.Context) (err error) { func (s *Shard) UpdateID(ctx context.Context) (err error) {
var metabaseOpened bool
var idFromMetabase []byte var idFromMetabase []byte
if err = s.metaBase.Open(ctx, false); err != nil { metabaseOpened := !s.GetMode().NoMetabase()
if err = s.metaBase.Open(ctx, s.GetMode()); err != nil {
err = fmt.Errorf("failed to open metabase: %w", err) err = fmt.Errorf("failed to open metabase: %w", err)
} else { metabaseOpened = false
metabaseOpened = true
} }
if metabaseOpened { if metabaseOpened {
defer func() { defer func() {

View file

@ -8,6 +8,7 @@ import (
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/blobstor/teststore" "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/blobstor/teststore"
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/internal/testutil" "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/internal/testutil"
meta "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/metabase" meta "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/metabase"
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/shard/mode"
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/writecache" "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/writecache"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
@ -75,7 +76,7 @@ func benchmarkPutPar(b *testing.B, cache writecache.Cache, size uint64) {
} }
func benchmarkPutPrepare(b *testing.B, cache writecache.Cache) { func benchmarkPutPrepare(b *testing.B, cache writecache.Cache) {
require.NoError(b, cache.Open(context.Background(), false), "opening") require.NoError(b, cache.Open(context.Background(), mode.ReadWrite), "opening")
require.NoError(b, cache.Init(), "initializing") require.NoError(b, cache.Init(), "initializing")
} }

View file

@ -95,8 +95,14 @@ func (c *cache) DumpInfo() Info {
} }
// Open opens and initializes database. Reads object counters from the ObjectCounters instance. // Open opens and initializes database. Reads object counters from the ObjectCounters instance.
func (c *cache) Open(_ context.Context, readOnly bool) error { func (c *cache) Open(_ context.Context, mode mode.Mode) error {
err := c.openStore(readOnly) c.modeMtx.Lock()
defer c.modeMtx.Unlock()
c.mode = mode
if mode.NoMetabase() {
return nil
}
err := c.openStore(mode.ReadOnly())
if err != nil { if err != nil {
return metaerr.Wrap(err) return metaerr.Wrap(err)
} }

View file

@ -202,7 +202,7 @@ func newCache[Option any](
mb := meta.New( mb := meta.New(
meta.WithPath(filepath.Join(dir, "meta")), meta.WithPath(filepath.Join(dir, "meta")),
meta.WithEpochState(dummyEpoch{})) meta.WithEpochState(dummyEpoch{}))
require.NoError(t, mb.Open(context.Background(), false)) require.NoError(t, mb.Open(context.Background(), mode.ReadWrite))
require.NoError(t, mb.Init()) require.NoError(t, mb.Init())
bs := blobstor.New(blobstor.WithStorages([]blobstor.SubStorage{ bs := blobstor.New(blobstor.WithStorages([]blobstor.SubStorage{
@ -213,11 +213,11 @@ func newCache[Option any](
fstree.WithDirNameLen(1)), fstree.WithDirNameLen(1)),
}, },
})) }))
require.NoError(t, bs.Open(context.Background(), false)) require.NoError(t, bs.Open(context.Background(), mode.ReadWrite))
require.NoError(t, bs.Init()) require.NoError(t, bs.Init())
wc := createCacheFn(t, smallSize, mb, bs, opts...) wc := createCacheFn(t, smallSize, mb, bs, opts...)
require.NoError(t, wc.Open(context.Background(), false)) require.NoError(t, wc.Open(context.Background(), mode.ReadWrite))
require.NoError(t, wc.Init()) require.NoError(t, wc.Init())
// First set mode for metabase and blobstor to prevent background flushes. // First set mode for metabase and blobstor to prevent background flushes.

View file

@ -0,0 +1,30 @@
package writecache
import (
"context"
"testing"
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/shard/mode"
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/util/logger/test"
"github.com/stretchr/testify/require"
)
func TestMode(t *testing.T) {
t.Parallel()
wc := New(
WithLogger(test.NewLogger(t)),
WithFlushWorkersCount(2),
WithPath(t.TempDir()))
require.NoError(t, wc.Open(context.Background(), mode.DegradedReadOnly))
require.Nil(t, wc.(*cache).db)
require.NoError(t, wc.Init())
require.Nil(t, wc.(*cache).db)
require.NoError(t, wc.Close())
require.NoError(t, wc.Open(context.Background(), mode.Degraded))
require.Nil(t, wc.(*cache).db)
require.NoError(t, wc.Init())
require.Nil(t, wc.(*cache).db)
require.NoError(t, wc.Close())
}

View file

@ -39,7 +39,7 @@ type Cache interface {
Seal(context.Context, bool) error Seal(context.Context, bool) error
Init() error Init() error
Open(ctx context.Context, readOnly bool) error Open(ctx context.Context, mode mode.Mode) error
Close() error Close() error
} }