From 0cb0fc17355f7c26e9229f157673330664cea59b Mon Sep 17 00:00:00 2001 From: Dmitrii Stepanov Date: Wed, 27 Dec 2023 08:20:15 +0300 Subject: [PATCH] [#569] writecache: Allow to seal writecache after flush Signed-off-by: Dmitrii Stepanov --- .../modules/control/flush_cache.go | 8 +- pkg/local_object_storage/engine/writecache.go | 10 +- pkg/local_object_storage/shard/writecache.go | 9 +- pkg/local_object_storage/writecache/flush.go | 91 ++++++++++++++---- .../writecache/flush_test.go | 9 +- pkg/local_object_storage/writecache/mode.go | 6 +- .../writecache/storage.go | 24 +++-- .../writecache/writecache.go | 2 +- pkg/services/control/server/flush_cache.go | 1 + pkg/services/control/service.pb.go | Bin 194862 -> 195243 bytes pkg/services/control/service.proto | 2 + pkg/services/control/service_frostfs.pb.go | Bin 89815 -> 89904 bytes 12 files changed, 121 insertions(+), 41 deletions(-) diff --git a/cmd/frostfs-cli/modules/control/flush_cache.go b/cmd/frostfs-cli/modules/control/flush_cache.go index 48be393dc..7f632e9fc 100644 --- a/cmd/frostfs-cli/modules/control/flush_cache.go +++ b/cmd/frostfs-cli/modules/control/flush_cache.go @@ -8,6 +8,8 @@ import ( "github.com/spf13/cobra" ) +const sealFlag = "seal" + var flushCacheCmd = &cobra.Command{ Use: "flush-cache", Short: "Flush objects from the write-cache to the main storage", @@ -18,7 +20,10 @@ var flushCacheCmd = &cobra.Command{ func flushCache(cmd *cobra.Command, _ []string) { pk := key.Get(cmd) - req := &control.FlushCacheRequest{Body: new(control.FlushCacheRequest_Body)} + seal, _ := cmd.Flags().GetBool(sealFlag) + req := &control.FlushCacheRequest{Body: &control.FlushCacheRequest_Body{ + Seal: seal, + }} req.Body.Shard_ID = getShardIDList(cmd) signRequest(cmd, pk, req) @@ -44,6 +49,7 @@ func initControlFlushCacheCmd() { ff := flushCacheCmd.Flags() ff.StringSlice(shardIDFlag, nil, "List of shard IDs in base58 encoding") ff.Bool(shardAllFlag, false, "Process all shards") + ff.Bool(sealFlag, false, "Writecache will be left in read-only mode after flush completed") flushCacheCmd.MarkFlagsMutuallyExclusive(shardIDFlag, shardAllFlag) } diff --git a/pkg/local_object_storage/engine/writecache.go b/pkg/local_object_storage/engine/writecache.go index 00a40105e..d92a86f5d 100644 --- a/pkg/local_object_storage/engine/writecache.go +++ b/pkg/local_object_storage/engine/writecache.go @@ -17,6 +17,7 @@ import ( type FlushWriteCachePrm struct { shardID *shard.ID ignoreErrors bool + seal bool } // SetShardID is an option to set shard ID. @@ -26,11 +27,16 @@ func (p *FlushWriteCachePrm) SetShardID(id *shard.ID) { p.shardID = id } -// SetIgnoreErrors sets errors ignore flag.. +// SetIgnoreErrors sets errors ignore flag. func (p *FlushWriteCachePrm) SetIgnoreErrors(ignore bool) { p.ignoreErrors = ignore } +// SetSeal sets seal flag. +func (p *FlushWriteCachePrm) SetSeal(v bool) { + p.seal = v +} + // FlushWriteCacheRes groups the resulting values of FlushWriteCache operation. type FlushWriteCacheRes struct{} @@ -40,6 +46,7 @@ func (e *StorageEngine) FlushWriteCache(ctx context.Context, p FlushWriteCachePr trace.WithAttributes( attribute.String("shard)id", p.shardID.String()), attribute.Bool("ignore_errors", p.ignoreErrors), + attribute.Bool("seal", p.seal), )) defer span.End() @@ -53,6 +60,7 @@ func (e *StorageEngine) FlushWriteCache(ctx context.Context, p FlushWriteCachePr var prm shard.FlushWriteCachePrm prm.SetIgnoreErrors(p.ignoreErrors) + prm.SetSeal(p.seal) return FlushWriteCacheRes{}, sh.FlushWriteCache(ctx, prm) } diff --git a/pkg/local_object_storage/shard/writecache.go b/pkg/local_object_storage/shard/writecache.go index 7ce279c54..4e57a0497 100644 --- a/pkg/local_object_storage/shard/writecache.go +++ b/pkg/local_object_storage/shard/writecache.go @@ -12,6 +12,7 @@ import ( // FlushWriteCachePrm represents parameters of a `FlushWriteCache` operation. type FlushWriteCachePrm struct { ignoreErrors bool + seal bool } // SetIgnoreErrors sets the flag to ignore read-errors during flush. @@ -19,6 +20,11 @@ func (p *FlushWriteCachePrm) SetIgnoreErrors(ignore bool) { p.ignoreErrors = ignore } +// SetSeal sets the flag to left writecache in read-only mode after flush. +func (p *FlushWriteCachePrm) SetSeal(v bool) { + p.seal = v +} + // errWriteCacheDisabled is returned when an operation on write-cache is performed, // but write-cache is disabled. var errWriteCacheDisabled = errors.New("write-cache is disabled") @@ -29,6 +35,7 @@ func (s *Shard) FlushWriteCache(ctx context.Context, p FlushWriteCachePrm) error trace.WithAttributes( attribute.String("shard_id", s.ID().String()), attribute.Bool("ignore_errors", p.ignoreErrors), + attribute.Bool("seal", p.seal), )) defer span.End() @@ -47,5 +54,5 @@ func (s *Shard) FlushWriteCache(ctx context.Context, p FlushWriteCachePrm) error return ErrDegradedMode } - return s.writeCache.Flush(ctx, p.ignoreErrors) + return s.writeCache.Flush(ctx, p.ignoreErrors, p.seal) } diff --git a/pkg/local_object_storage/writecache/flush.go b/pkg/local_object_storage/writecache/flush.go index f4ceec8c8..17dcc1107 100644 --- a/pkg/local_object_storage/writecache/flush.go +++ b/pkg/local_object_storage/writecache/flush.go @@ -12,6 +12,7 @@ import ( "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/blobstor/common" "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/internal/metaerr" 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-observability/tracing" objectSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object" oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id" @@ -33,6 +34,8 @@ const ( defaultFlushInterval = time.Second ) +var errIterationCompleted = errors.New("iteration completed") + // runFlushLoop starts background workers which periodically flush objects to the blobstor. func (c *cache) runFlushLoop(ctx context.Context) { if c.disableBackgroundFlush { @@ -229,7 +232,7 @@ func (c *cache) workerFlushSmall(ctx context.Context) { continue } - c.deleteFromDB(objInfo.addr) + c.deleteFromDB(objInfo.addr, true) } } @@ -270,19 +273,29 @@ func (c *cache) flushObject(ctx context.Context, obj *objectSDK.Object, data []b } // Flush flushes all objects from the write-cache to the main storage. -// Write-cache must be in readonly mode to ensure correctness of an operation and -// to prevent interference with background flush workers. -func (c *cache) Flush(ctx context.Context, ignoreErrors bool) error { - ctx, span := tracing.StartSpanFromContext(ctx, "Flush", +func (c *cache) Flush(ctx context.Context, ignoreErrors, seal bool) error { + ctx, span := tracing.StartSpanFromContext(ctx, "writecache.Flush", trace.WithAttributes( attribute.Bool("ignore_errors", ignoreErrors), + attribute.Bool("seal", seal), )) defer span.End() - c.modeMtx.RLock() - defer c.modeMtx.RUnlock() + c.modeMtx.Lock() // exclusive lock to not to conflict with background flush + defer c.modeMtx.Unlock() - return c.flush(ctx, ignoreErrors) + if err := c.flush(ctx, ignoreErrors); err != nil { + return err + } + + if seal { + m := c.mode | mode.ReadOnly + if err := c.setMode(ctx, m, ignoreErrors); err != nil { + return err + } + c.metrics.SetMode(m) + } + return nil } func (c *cache) flush(ctx context.Context, ignoreErrors bool) error { @@ -290,13 +303,53 @@ func (c *cache) flush(ctx context.Context, ignoreErrors bool) error { return err } - return c.db.View(func(tx *bbolt.Tx) error { + var last string + for { + batch, err := c.readNextDBBatch(ignoreErrors, last) + if err != nil { + return err + } + if len(batch) == 0 { + break + } + for _, item := range batch { + var obj objectSDK.Object + if err := obj.Unmarshal(item.data); err != nil { + c.reportFlushError(logs.FSTreeCantDecodeDBObjectAddress, item.address, metaerr.Wrap(err)) + if ignoreErrors { + continue + } + return err + } + + if err := c.flushObject(ctx, &obj, item.data, StorageTypeDB); err != nil { + return err + } + c.deleteFromDB(item.address, false) + } + last = batch[len(batch)-1].address + } + return nil +} + +type batchItem struct { + data []byte + address string +} + +func (c *cache) readNextDBBatch(ignoreErrors bool, last string) ([]batchItem, error) { + const batchSize = 100 + var batch []batchItem + err := c.db.View(func(tx *bbolt.Tx) error { var addr oid.Address b := tx.Bucket(defaultBucket) cs := b.Cursor() - for k, data := cs.Seek(nil); k != nil; k, data = cs.Next() { + for k, data := cs.Seek([]byte(last)); k != nil; k, data = cs.Next() { sa := string(k) + if sa == last { + continue + } if err := addr.DecodeString(sa); err != nil { c.reportFlushError(logs.FSTreeCantDecodeDBObjectAddress, sa, metaerr.Wrap(err)) if ignoreErrors { @@ -305,19 +358,15 @@ func (c *cache) flush(ctx context.Context, ignoreErrors bool) error { return err } - var obj objectSDK.Object - if err := obj.Unmarshal(data); err != nil { - c.reportFlushError(logs.FSTreeCantDecodeDBObjectAddress, sa, metaerr.Wrap(err)) - if ignoreErrors { - continue - } - return err - } - - if err := c.flushObject(ctx, &obj, data, StorageTypeDB); err != nil { - return err + batch = append(batch, batchItem{data: bytes.Clone(data), address: sa}) + if len(batch) == batchSize { + return errIterationCompleted } } return nil }) + if err == nil || errors.Is(err, errIterationCompleted) { + return batch, nil + } + return nil, err } diff --git a/pkg/local_object_storage/writecache/flush_test.go b/pkg/local_object_storage/writecache/flush_test.go index 20db1de95..4a243c5ec 100644 --- a/pkg/local_object_storage/writecache/flush_test.go +++ b/pkg/local_object_storage/writecache/flush_test.go @@ -147,7 +147,7 @@ func runFlushTest[Option any]( require.NoError(t, bs.SetMode(mode.ReadWrite)) require.NoError(t, mb.SetMode(mode.ReadWrite)) - require.NoError(t, wc.Flush(context.Background(), false)) + require.NoError(t, wc.Flush(context.Background(), false, false)) check(t, mb, bs, objects) }) @@ -159,8 +159,6 @@ func runFlushTest[Option any]( // Blobstor is read-only, so we expect en error from `flush` here. require.Error(t, wc.SetMode(mode.Degraded)) - // First move to read-only mode to close background workers. - require.NoError(t, wc.SetMode(mode.ReadOnly)) require.NoError(t, bs.SetMode(mode.ReadWrite)) require.NoError(t, mb.SetMode(mode.ReadWrite)) require.NoError(t, wc.SetMode(mode.Degraded)) @@ -177,14 +175,13 @@ func runFlushTest[Option any]( objects := putObjects(t, wc) f.InjectFn(t, wc) - require.NoError(t, wc.SetMode(mode.ReadOnly)) require.NoError(t, bs.SetMode(mode.ReadWrite)) require.NoError(t, mb.SetMode(mode.ReadWrite)) require.Equal(t, uint32(0), errCount.Load()) - require.Error(t, wc.Flush(context.Background(), false)) + require.Error(t, wc.Flush(context.Background(), false, false)) require.Greater(t, errCount.Load(), uint32(0)) - require.NoError(t, wc.Flush(context.Background(), true)) + require.NoError(t, wc.Flush(context.Background(), true, false)) check(t, mb, bs, objects) }) diff --git a/pkg/local_object_storage/writecache/mode.go b/pkg/local_object_storage/writecache/mode.go index e3ff2286b..7c6439fe9 100644 --- a/pkg/local_object_storage/writecache/mode.go +++ b/pkg/local_object_storage/writecache/mode.go @@ -25,7 +25,7 @@ func (c *cache) SetMode(m mode.Mode) error { c.modeMtx.Lock() defer c.modeMtx.Unlock() - err := c.setMode(ctx, m) + err := c.setMode(ctx, m, true) if err == nil { c.metrics.SetMode(m) } @@ -33,12 +33,12 @@ func (c *cache) SetMode(m mode.Mode) error { } // setMode applies new mode. Must be called with cache.modeMtx lock taken. -func (c *cache) setMode(ctx context.Context, m mode.Mode) error { +func (c *cache) setMode(ctx context.Context, m mode.Mode, ignoreErrors bool) error { var err error turnOffMeta := m.NoMetabase() if turnOffMeta && !c.mode.NoMetabase() { - err = c.flush(ctx, true) + err = c.flush(ctx, ignoreErrors) if err != nil { return err } diff --git a/pkg/local_object_storage/writecache/storage.go b/pkg/local_object_storage/writecache/storage.go index 5c25a3b33..6cc3b06d0 100644 --- a/pkg/local_object_storage/writecache/storage.go +++ b/pkg/local_object_storage/writecache/storage.go @@ -67,14 +67,24 @@ func (c *cache) openStore(readOnly bool) error { return nil } -func (c *cache) deleteFromDB(key string) { +func (c *cache) deleteFromDB(key string, batched bool) { var recordDeleted bool - err := c.db.Batch(func(tx *bbolt.Tx) error { - b := tx.Bucket(defaultBucket) - key := []byte(key) - recordDeleted = b.Get(key) != nil - return b.Delete(key) - }) + var err error + if batched { + err = c.db.Batch(func(tx *bbolt.Tx) error { + b := tx.Bucket(defaultBucket) + key := []byte(key) + recordDeleted = b.Get(key) != nil + return b.Delete(key) + }) + } else { + err = c.db.Update(func(tx *bbolt.Tx) error { + b := tx.Bucket(defaultBucket) + key := []byte(key) + recordDeleted = b.Get(key) != nil + return b.Delete(key) + }) + } if err == nil { c.metrics.Evict(StorageTypeDB) diff --git a/pkg/local_object_storage/writecache/writecache.go b/pkg/local_object_storage/writecache/writecache.go index 3d3501969..0549c27f7 100644 --- a/pkg/local_object_storage/writecache/writecache.go +++ b/pkg/local_object_storage/writecache/writecache.go @@ -35,7 +35,7 @@ type Cache interface { SetMode(mode.Mode) error SetLogger(*logger.Logger) DumpInfo() Info - Flush(context.Context, bool) error + Flush(context.Context, bool, bool) error Init() error Open(ctx context.Context, readOnly bool) error diff --git a/pkg/services/control/server/flush_cache.go b/pkg/services/control/server/flush_cache.go index 9ead530db..67ffa1f2c 100644 --- a/pkg/services/control/server/flush_cache.go +++ b/pkg/services/control/server/flush_cache.go @@ -18,6 +18,7 @@ func (s *Server) FlushCache(ctx context.Context, req *control.FlushCacheRequest) for _, shardID := range s.getShardIDList(req.GetBody().GetShard_ID()) { var prm engine.FlushWriteCachePrm prm.SetShardID(shardID) + prm.SetSeal(req.GetBody().GetSeal()) _, err = s.s.FlushWriteCache(ctx, prm) if err != nil { diff --git a/pkg/services/control/service.pb.go b/pkg/services/control/service.pb.go index 8b8739aba9da08599e07b164935b98ac3fa63783..90e643e2957a7b3ee9edca179999666ef1a3047b 100644 GIT binary patch delta 4886 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 KiaGS!3;zO^%DLMB delta 4522 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 diff --git a/pkg/services/control/service.proto b/pkg/services/control/service.proto index cbc3aaf54..e3b507387 100644 --- a/pkg/services/control/service.proto +++ b/pkg/services/control/service.proto @@ -280,6 +280,8 @@ message FlushCacheRequest { message Body { // ID of the shard. repeated bytes shard_ID = 1; + // If true, then writecache will be left in read-only mode after flush completed. + bool seal = 2; } Body body = 1; diff --git a/pkg/services/control/service_frostfs.pb.go b/pkg/services/control/service_frostfs.pb.go index e163e54b7a76a87fd9c060b57fa95c29e00a2dc4..3ace081e46092e07c6f512da4b6cb75c0cda0bf9 100644 GIT binary patch delta 49 zcmcb