From 6cd806f99873b84e9a235b4ace70cf89abdfb69e Mon Sep 17 00:00:00 2001 From: Evgenii Stratonikov Date: Wed, 25 Jan 2023 15:44:44 +0300 Subject: [PATCH] [#82] services/tree: Save last synchronized height in a persistent storage Remember the last synchronized height and use it after service restart. Signed-off-by: Evgenii Stratonikov --- CHANGELOG.md | 1 + pkg/services/tree/service.go | 8 +++----- pkg/services/tree/sync.go | 35 ++++++++++++++--------------------- 3 files changed, 18 insertions(+), 26 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 74a7a33b3c..f2875ea4f3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ Changelog for FrostFS Node - Reload config for pprof and metrics on SIGHUP in `neofs-node` (#1868) - Multiple configs support (#44) - Parameters `nns-name` and `nns-zone` for command `frostfs-cli container create` (#37) +- Tree service now saves the last synchronization height which persists across restarts (#82) ### Changed - Change `frostfs_node_engine_container_size` to counting sizes of logical objects diff --git a/pkg/services/tree/service.go b/pkg/services/tree/service.go index a01072cbc0..3176858e2b 100644 --- a/pkg/services/tree/service.go +++ b/pkg/services/tree/service.go @@ -31,10 +31,8 @@ type Service struct { syncChan chan struct{} syncPool *ants.Pool - // cnrMap maps contrainer and tree ID to the minimum height which was fetched from _each_ client. - // This allows us to better handle split-brain scenario, because we always synchronize - // from the last seen height. The inner map is read-only and should not be modified in-place. - cnrMap map[cidSDK.ID]map[string]uint64 + // cnrMap contains existing (used) container IDs. + cnrMap map[cidSDK.ID]struct{} // cnrMapMtx protects cnrMap cnrMapMtx sync.Mutex } @@ -63,7 +61,7 @@ func New(opts ...Option) *Service { s.replicateLocalCh = make(chan applyOp) s.replicationTasks = make(chan replicationTask, s.replicatorWorkerCount) s.containerCache.init(s.containerCacheSize) - s.cnrMap = make(map[cidSDK.ID]map[string]uint64) + s.cnrMap = make(map[cidSDK.ID]struct{}) s.syncChan = make(chan struct{}) s.syncPool, _ = ants.NewPool(defaultSyncWorkerCount) diff --git a/pkg/services/tree/sync.go b/pkg/services/tree/sync.go index d6646ab946..a1bab61de5 100644 --- a/pkg/services/tree/sync.go +++ b/pkg/services/tree/sync.go @@ -86,31 +86,24 @@ func (s *Service) synchronizeAllTrees(ctx context.Context, cid cid.ID) error { return fmt.Errorf("could not fetch tree ID list: %w", outErr) } - s.cnrMapMtx.Lock() - oldStatus := s.cnrMap[cid] - s.cnrMapMtx.Unlock() - - syncStatus := map[string]uint64{} - for i := range treesToSync { - syncStatus[treesToSync[i]] = 0 - } - for tid := range oldStatus { - if _, ok := syncStatus[tid]; ok { - syncStatus[tid] = oldStatus[tid] - } - } - for _, tid := range treesToSync { - h := s.synchronizeTree(ctx, d, syncStatus[tid], tid, nodes) - if syncStatus[tid] < h { - syncStatus[tid] = h + h, err := s.forest.TreeLastSyncHeight(d.CID, tid) + if err != nil && !errors.Is(err, pilorama.ErrTreeNotFound) { + s.log.Warn("could not get last synchronized height for a tree", + zap.Stringer("cid", d.CID), + zap.String("tree", tid)) + continue + } + newHeight := s.synchronizeTree(ctx, d, h, tid, nodes) + if h < newHeight { + if err := s.forest.TreeUpdateLastSyncHeight(d.CID, tid, newHeight); err != nil { + s.log.Warn("could not update last synchronized height for a tree", + zap.Stringer("cid", d.CID), + zap.String("tree", tid)) + } } } - s.cnrMapMtx.Lock() - s.cnrMap[cid] = syncStatus - s.cnrMapMtx.Unlock() - return nil }